Skip to content

Latest commit

 

History

History
12797 lines (10695 loc) · 544 KB

init.org

File metadata and controls

12797 lines (10695 loc) · 544 KB

A Life Configuring Emacs

#

1 Abstract

badge:A_Life|Configuring_Emacs|success|https://github.com/alhassy/emacs.d|gnu-emacs

badge:A_Life_Configuring|Emacs|success|https://alhassy.github.io/emacs.d/|gnu

Abstract

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

gnu 3 license badge tweet:https://alhassy.com/emacs.d/

badge:author|musa_al-hassy|purple|https://alhassy.github.io/|nintendo-3ds badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee

This article is about how I like to do things —/I’m not insisting others should/ do things this way.

2 Meta-setup

2.1 Blog/HTML Setup

2.2 Github Actions

The following creates the “Github Actions Workflow” file; this way, Github will run your tests every time you commit ^_^

Below I’m using main as the name of the main branch; if you use master as the name, then change that or otherwise the tests will not trigger automatically after push!

# This workflow will do a clean install of dependencies and run tests
# For more information see: https://help.github.com/actions/language-and-framework-guides/

name: Lifemacs Loads Successfully

# Controls when the action will run.
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Set up Emacs
        uses: purcell/[email protected]
        with:
          # The version of Emacs to install, e.g. "24.3", or "snapshot" for a recent development version.
          version: 27.1 # optional

      # Runs a single command using the runners shell
      # - name: Run a one-line script
      #  run: echo Hello, world!

      # Runs a set of commands using the runners shell
      # - name: Run a multi-line script
      #  run: |
      #    echo Add other actions to build,
      #    echo test, and deploy your project.

      - name: where am I and what is here
        run: |
          pwd
          ls

      - name: Attempting startup & basic test ...
        run: time emacs -nw --batch --eval='(let
                                (
                                 (user-emacs-directory default-directory))
                              (message "Default directory" )
                              (message default-directory)
                              (setq url-show-status nil)
                              (package-initialize)
                              (load-file "init.el")
                              (message "\n 🤤 Startup Successful! 🤩")
                              (when nil "It seems this results in a non-existent Agenda issue when run by GHA; ignoring for now."
                                  (message "\nCheck we can do something basic, like getting pretty HTML from an Org file\n")
                                  (find-file "init.org")
                                  (org-html-export-to-html)
                                  (message "\n 🤤 HTML file generated successfully! 🤩\n")))'

TODO: Build HTML export as a minimal test that things work as expected.

(find-file “init.org”) (org-html-export-to-html) (message “\n 🤤 HTML Export Successful! 🤩”)

(unless noninteractive …) ;; Use this to omit stuff from the Github Actions test

2.3 Personal instructions for a new machine

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.
  1. Use brew to get Emacs:

    #

    # https://github.com/d12frosted/homebrew-emacs-plus
    $ brew tap d12frosted/emacs-plus
    $ brew reinstall emacs-plus@30  --with-xwidgets --with-imagemagick --with-dbus --with-debug --with-no-frame-refocus --with-native-comp --with-savchenkovaleriy-big-sur-3d-icon --with-poll
        

    #

    #

    #

    #

  2. Then    make   the command emacs available via the terminal —required if doing any melpa development.
    ln -s /opt/homebrew/opt/emacs-plus@30/Emacs.app /Applications
        
  3. Why --with-imagemagick?

    ;; This lets us change the size of images when shown in Org-mode.

    (setq org-image-actual-width nil)
        

    Note that only the :width option is used for in-Org image preview.

  4. Why --with-xwidgets?

    We get a full-fledged Internet browser within Emacs.

    ;; Clicking on a URL, or running M-x browse-url, should open the URL *within* Emacs.
    (setq browse-url-browser-function #'xwidget-webkit-browse-url)
    
    ;; (use-package xwwp) ;; Enhance the Emacs xwidget-webkit browser
        

    Related: doc:goto-address-mode is useful whenever you have a buffer full of “http…” URLs (e.g.., a JSON file): It makes them into clickable buttons or via C-c RET.

    • How about EAF or nyxt?

      An alternative to xwidget-webkit is EAF, sadly this does not work well with MacOS.

      EAF essentially makes Emacs a window manager that runs other GUI apps —as such, EAF buffers are not classic Emacs buffers (and so your favourite text commands are useless).

      There is also:

      nyxt  ≅  the web running common lisp instead of JS  ≅  an Emacs backed-by common lisp
    • Tell me more about xwidget-webkit

      I’ve found that the only two applications I regularly have open are Emacs and a browser —and Emacs has a modern browser, so might as well use that in Emacs.

      • Downsides of Emacs as a browser: Some webpages, such as Slack, try to be an editor and so I’m using a Slack editor insider a web browser inside an text editor (Emacs). As such, sometimes the lines between editor and browser need to be explicitly demarcated; e.g., via xwidget-webkit-edit-mode.
      • “xwidget ≈ eXternal widget” lets us embed GTK widgets inside an Emacs window; e.g., we can insert fancy buttons via xwidget-insert, and a browser using doc:xwidget-webkit-browse-url.
      • For history and info on xwidget, see the original patch; See also: https://webkitgtk.org/
  5. Install git: brew install git
  6. 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 doc:helm-do-grep-ag, useful for finding definitions).

#

since they override the beloved Emacs M-<,> keys.

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.

Finally, see the Prose/Unicode section, we need to manually install the Symbol font for subscripts.

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

3.1 Mini-tutorial on Org-mode

Export In Emacs, press kbd:C-c_C-e_h_o to obtain an HTML webpage —/like this one!/— of the Org-mode markup; use kbd: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 kbd: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.
    SyntaxResult
    /Emphasise/, italicsEmphasise
    *Strong*, boldStrong
    */very strongly/*, bold italics/very strongly/
    =verbatim=, monospaced typewriterverbatim
    +deleted+deleted
    _inserted_inserted
    super^{script}edsuperscripted
    sub_{scripted}edsubscripteded
    • 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

badge:org-special-block-extras|2.0|informational|https://github.com/alhassy/org-special-block-extras|Gnu-Emacs provides support for numerous other kinds of markup elements, such as red:hello which becomes “ red:hello ”.


Working with tables

#+ATTR_HTML: :width 100%
#+name: my-tbl
#+caption: Example table
| Who? | What? |
|------+-------|
| me   | Emacs |
| you  | Org   |

Note the horizontal rule makes a header row and is formed by typing | - then pressing kbd:TAB. You can kbd:TAB between cells.

  • You can make an empty table with C-c |, which is just doc: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

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

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:

♯+RESULTS: bold 12345 (strikethrough) slanted

The #+RESULTS: is obtained by pressing kbd: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:

https://alhassy.github.io/emacs.d/images/literate-programming.png



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.

3.2 Intro to why Emacs

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.

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 kbd:M-x info-apropos or kbd: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! That is, while other editors have a single-item clipboard, Emacs has an infinite clipboard that allows undoing to any historical state.

;; Allow tree-semantics for undo operations.
(use-package undo-tree
  :bind ("C-x u" . undo-tree-visualize)
  :config
    ;; 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)

    ;; Prevent undo tree files from polluting your git repo
    (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))))

;; Always have it on
(global-undo-tree-mode)

;; 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 kbd: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

3.3   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

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.

3.4 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: kbd:M-x dired ⟨RET⟩ M-x wdired-change-to-wdired-mode now simply perform a usual find-and-replace, then save with the usual kbd: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.

3.5 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 <a href=”kbd:M-{ C-u 80 - RET M-} C-u 80 - RET”>kbd: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 kbd: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 kbd:C-a/e, kbd:C-d, kbd:C-k/y, kbd:C-o, kbd:C-p/n and even kbd:C-t for transposing two characters. ⇐

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

3.7 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

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

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

images/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 kbd:C-c_C-v_C-t (doc: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.

3.10 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 concluding 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!

4 Booting Up

Let’s decide on where we want to setup our declarations for personalising Emacs to our needs. Then, let’s bootstrap Emacs’ primitive 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.

4.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 kbd: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 kbd: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 kbd: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 kbd:M-x eval-buffer, doc: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, kbd: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.

~/.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!

Speaking of local variables, let’s always load ones we’ve already marked as safe —see the bottom of the source of this file for an example of local variables. ( At one point, all my files had locals! )
(setq enable-local-variables :safe)

4.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 "[email protected]")

For some fun, run this cute method.

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

4.3 Emacs Package Manager

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

  • Interactively: kbd:M-x list-packages to see all melpa packages that can install
    • Press kbd:Enter on a package to see its description.
  • Or more quickly, to install, say, unicode fonts: <a href=”kbd:M-x package-install RET unicode-fonts RET”>kbd: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 doc: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.
If there are issues with loading the archives, say, “Failed to download ‘gnu’ archive.” then ensure you can both read and write, recursively, to your .emacs.d/ E.g., within emacs try to execute (package-refresh-contents) and you’ll observe a permissions error.

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

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

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

(use-package dash) ;; “A modern list library for Emacs”
(use-package s)    ;; “The long lost Emacs string manipulation library”.
(use-package f)    ;; Library for working with system files; ;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden?

(defvar my/personal-machine?
  (equal "Musa’s MacBook Air " (s-collapse-whitespace (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?))

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.

<<undo-tree-setup>>

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


link-here:Quelpa 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+
  :quelpa (info+ :fetcher wiki :url "https://www.emacswiki.org/emacs/info%2b.el"))

4.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 system-packages)

;; 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.
(run-with-idle-timer 5 nil #'system-packages-update) ;; ≈ (async-shell-command "brew update && brew upgrade")

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

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

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.
(setq system-packages-package-manager 'brew)

;; If the given system package doesn't exist; install it.
;; (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.

;; ;; Use “brew cask install” instead of “brew install” for installing programs.;
;; (setf (nth 2 (assoc 'brew system-packages-supported-package-managers))
;;      '(install . "brew cask install"))

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 kbd:⌘-a_r relaunches 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”.

(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”).

4.4.1 Don’t show updating/installation shell buffers

;; By default, say, (async-shell-command "date") produces a buffer
;; with the result. In general, such commands in my init.el are for
;; updating/installing things to make sure I have the same up-to-date
;; setup where-ever I use my Emacs. As such, I don't need to see such buffers.
(add-to-list 'display-buffer-alist
             '("\\*Async Shell Command\\*.*" display-buffer-no-window))

;; For an approach that does not inhibit async-shell-command this way,
;; see https://emacs.stackexchange.com/questions/299/how-can-i-run-an-async-process-in-the-background-without-popping-up-a-buffer

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

;; https://emacs.stackexchange.com/questions/4090/org-mode-cannot-find-pdflatex-using-mac-os

(defun set-exec-path-from-shell-PATH ()
  "Sets the exec-path to the same value used by the user shell"
  (let ((path-from-shell
         (replace-regexp-in-string
          "[[:space:]\n]*$" ""
          (shell-command-to-string "$SHELL -l -c 'echo $PATH'"))))
    (setenv "PATH" path-from-shell)
    (setq exec-path (split-string path-from-shell path-separator))))

;; call function now
(set-exec-path-from-shell-PATH)

4.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")

4.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 (doc: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 doc: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
 :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))

;; When I want to see the TOC of an Org file, show me down to 3 subheadings.
(setq org-imenu-depth 7)

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!
;; If there is no symbol at the cursor, use the last used words instead.
(setq helm-swoop-pre-input-function
    (lambda ()
      (let (($pre-input (thing-at-point 'symbol)))
        (if (eq (length $pre-input) 0)
            helm-swoop-pattern ;; this variable keeps the last used words
          $pre-input))))

Press M-i after a search has executed to enable it for all buffers.

We can also limit our search to org files, or buffers of the same mode, or buffers belonging to the same project!

Note that on the Mac, I can still perform default Emacs search using Cmd+f.

  • There is a super duper neato search capability we can utilise – swiper.
  • We also use the counsel, a collection of completion, ivy, enhanced versions of common Emacs commands.
(use-package ivy ;; More powerful but ugly looking completetion framework.
                 ;; Helm is prettier.
                 ;; Needed for Counsel, below.
 :config

  ;; add ‘recentf-mode’ and bookmarks to ‘ivy-switch-buffer’.
  (setq ivy-use-virtual-buffers t)
  ;; number of result lines to display
  (setq ivy-height 10)
  ;; does not count candidates
  (setq ivy-count-format "")
  ;; no regexp by default
  (setq ivy-initial-inputs-alist nil))

(use-package counsel
  :bind*                              ; load when pressed
  (("C-s"     . swiper)
   ;; ("C-x C-r" . counsel-recentf)     ; search for recently edited
   ("<f1> l"  . counsel-find-library)   ; find an Emacs Lisp library
   ("<f2> u"  . counsel-unicode-char))) ; insert a unicode symbol using a pop-up

swiper is an alternative to Emacs’ default incremental search. It shows an overview of all matches –navigate using arrow keys. There is also a helm based version of swiper.

Lets also use doc:helm-do-grep-ag (kbd: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))

4.8 Org-Mode Administrivia

(shell-command "rm ~/.emacs.d/elpa/org-9.3/*.elc")
(byte-recompile-directory "~/.emacs.d/elpa/org-9.3/")

Easiest way get latest version of org-mode: M-x package-list-packages, then select org, then install.

https://stackoverflow.com/questions/31855904/emacs-sees-the-directory-with-the-new-org-mode-version-but-loads-the-old-versio

https://emacs.stackexchange.com/questions/27597/how-to-update-org-to-latest-version-using-package-repos-git-clone-in-ubuntu

#

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
    :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 "")
;; Other candidates:
;; (setq org-ellipsis "   📖")
;; (setq org-ellipsis "  ◦◦◦")
;; (setq org-ellipsis "  ⟨🫣⟩")
;; (setq org-ellipsis "  ⟨👀⟩")
;; (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 kbd:C-c_C-e. Documentations and screenshots at: https://alhassy.github.io/org-special-block-extras/

(defun org-special-block-extras-short-names ())
;;
;; org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks
(setq o--supported-blocks nil)

;; TODO org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks
;;
(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)

4.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 kbd: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…!

4.10 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 kC-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 Taking a tour of one’s edits 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.

;; Show hydras overlayed in the middle of the frame
(use-package hydra-posframe
  :disabled "TODO Fix me, breaking Github Actions test setup"
  :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: doc:my/defhydra —which combines doc:defhydra and doc:pretty-hydra-define.

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

4.10.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 )
;; :bind (("C-x o" . switch-window)
;;        ("C-x w" . switch-window-then-swap-buffer))

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

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

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

(use-package vterm) ;; Shell with a nearly universal compatibility with terminal applications 💝

;; "Intelligent" switching to vterm; eg creates it if it's not open, non-intrusive windowing, saves window setup, etc.
(use-package vterm-toggle
    :bind* ("C-t" . vterm-toggle))

4.12.1 Shell-pop

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

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

4.12.3 tldr

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, kbd:C-t tldr 𝒳 ⟨return⟩. )

(system-packages-ensure "tldr")

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

4.14 Summary of Utilities Provided

Since I’m using use-package, I can invoke M-x describe-personal-keybindings to see what key bindings I’ve defined. Since not all my bindings are via use-package, it does not yet cover all of my bindings.

We could run C-h b to see all our bindings —or M-x describe-personal-bindings to see only those set throughout our init file.

(use-package helm-descbinds
  :bind ("C-h b" . helm-descbinds))

Finally, we can observe which features are active in our current Emacs with,

(message "Features: %s" features)

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

4.16 Get Shell history within Emacs via Completing Read with Helm

Note that C-r in a terminal gets you to search shell history, here’s how to do it from Emacs in a more interactive fashion.
;; 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))

4.17 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 [[https://github.com/d12frosted/counsel-osx-app][counsel-osx-app]], whose name is misleading.

4.17.1 ?

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

5.1 High Speed Literate Programming

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

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

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

  • kbd:u for going to upwards to parent heading
    • kbd:i insert a new same-level heading below current heading.
  • kbd:c for cycling structure below current heading, or C for cycling global structure.
  • kbd:w refile current heading; options list pops-up to select which heading to move it to. Neato!
    • kbd: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)
              
  • kbd:n/p for next/previous visible heading.
  • kbd:f/b for jumping forward/backward to the next/previous same-level heading.
  • kbd:D/U move a heading down/up.
  • kbd:L/R recursively promote (move leftwards) or demote (more rightwards) a heading.
  • kbd:I/O clock In/Out to the task defined by the current heading.
    • Keep track of your work times!
    • kbd:v view agenda.
  • kbd:t/,/:/e to add a TODO state, priority level, tag, or effort estimate
    • kbd:1/2/3 to mark a heading with priority, highest to lowest.
  • kbd:^ sort children of current subtree; brings up a list of sorting options.
  • kbd:k/@/a to kill or mark or archive the current subtree
  • kbd: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 doc: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, kbd:? to see a complete list of keys available. ⇐

5.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 kbd:⌘+e to doc:org-edit-src-code / doc:org-edit-src-exit, but instead chose the more general doc:org-edit-special since, well, look at the tooltip documentation: This allows us to use kbd:⌘+e to ‘e’dit all kinds of Org entities —including footnotes and export blocks. ( Footnotes can be quickly produced with doc:org-footnote-new. )

5.1.3 Modifying kbd:⟨return⟩

  • kbd:C-⟨return⟩ , kbd:C-S-⟨return⟩ make a new heading where the latter marks it as a TODO.
  • By default kbd: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:

keymethodbehaviour
kbd:⟨return⟩doc:org-return-indentNewline with indentation
kbd:M-⟨return⟩doc:org-meta-returnNewline with new org item
kbd:C-M-⟨return⟩doc:electric-indent-just-newlineNewline, cursor at start
kbd:C-⟨return⟩doc:org-insert-heading-respect-contentNew heading after current content
kbd:C-S-⟨return⟩doc:org-insert-todo-heading-respect-contentDitto, but with a TODO marker

5.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 ;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-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
;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust
(ignore-errors (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 doc:add-to-list.

5.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 green:‘in your face’ reminder that there is local configuration that should have been loaded.

5.4 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.5 Unfold Org Headings when I perform a search

(setq org-fold-core-style 'overlays)

5.6 The “∶Disabled∶” tag —Stolen from AlBasmala.el, and improved

tldr: Add :Disabled: to a heading to render its contents in a <details> HTML element. If a heading has a :COLOR property, then that is used for the colour of the element, defaulting to pink.

(defmacro org-deftag (name args docstring &rest body)
  "Re-render an Org section in any way you like, by tagging the section with NAME.

That is to say, we essentially treat tags as functions that act on Org headings:
We redefine Org sections for the same purposes as Org special blocks.

The “arguments” to the function-tag can be declared as Org properties, then
the function can access them using the `o-properties' keyword as in
   (-let [(&plist :file :date :color) o-properties]
       (insert \"%s: %s\" file date))

Anyhow:
ARGS are the sequence of items seperated by underscores after the NAME of the new tag.
BODY is a form that may anaphorically mention:
- O-BACKEND: The backend we are exporting to, such as `latex' or `html'.
- O-HEADING: The string denoting the title of the tagged section heading.
- O-PROPERTIES: A plist of the Org properties at point.

DOCSTRING is mandatory; everything should be documented for future maintainability.

The result of this anaphoric macro is a symbolic function name `org-deftag/NAME',
which is added to `org-export-before-parsing-hook'.

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

Below is the motivating reason for inventing this macro. It is used:

     ** Interesting, but low-priority, content   :details_red:
     Blah blah blah blah blah blah blah blah blah blah blah.
     Blah blah blah blah blah blah blah blah blah blah blah.

Here is the actual implementation:

(org-deftag details (color)
   \"HTML export a heading as if it were a <details> block; COLOR is an optional
   argument indicating the background colour of the resulting block.\"
   (insert \"\n#+html:\"
           (format \"<details style=\\\"background-color: %s\\\">\" color)
           \"<summary>\" (s-replace-regexp \"^\** \" \"\" o-heading) \"</summary>\")
   (org-next-visible-heading 1)
   (insert \"#+html: </details>\"))

"
  (let ((func-name (intern (format "org-deftag/%s" name))))
    `(progn
       (cl-defun ,func-name (o-backend)
         ,docstring
         (outline-show-all)
         (org-map-entries
          (lambda ()
            (-let [(&alist ,@ (mapcar #'symbol-name args)) (map-apply (lambda (k v) (cons (downcase k) v)) (org-entry-properties (point)))]
              ;; MA: Maybe get rid of o-heading and o-properties and let people operate on raw Org secitons
              ;; as they do with org-agenda. That might provide a more unified approach.
              (let ((o-properties (map-into (map-apply (lambda (k v) (cons (intern (concat ":" (downcase k))) v)) (org-entry-properties (point))) 'plist))
                    (o-heading (progn (kill-line) (car kill-ring))))
                (if (not (s-contains? (format ":%s" (quote ,name)) o-heading 'ignoring-case))
                    (insert o-heading)
                  (setq o-heading (s-replace-regexp (format ":%s[^:]*:" (quote ,name)) "" o-heading))
                  ,@body)
                ;; Otherwise we impede on the auto-inserted “* footer :ignore:”
                (insert "\n"))))))
       (add-hook 'org-export-before-parsing-hook (quote ,func-name))
       )))


;; MA: This is new stuff.
(put 'org-deflink 'lisp-indent-function 'defun)
(put 'org-deftag 'lisp-indent-function 'defun)

       ;; Example use
       (org-deftag identity ()
         "Do nothing to Org headings"
         (insert o-heading)) ;; Wait, I think this strips tags?


       (org-deftag disabled (color)
         "Render the body of a heading in a <details> element, titled “Disabled”.

The heading remains in view, and so appears in the TOC."
         (insert "\n") (insert  o-heading) (insert "\n")
         (insert "\n#+html:"
                 (format "<div> <details class=\"float-child\" style=\"background-color: %s\">"
                         (or color "pink"))
                 "<summary> <strong> <font face=\"Courier\" size=\"3\" color=\"green\">"
                 "Details ﴾This is disabled, I'm not actively using it.﴿"
                 "</font> </strong> </summary>")
         ;; Something to consider: (org-set-property "UNNUMBERED" "nil")
         (org-next-visible-heading 1)
         (insert "#+html: </details> </div>"))

5.7 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
  :config (beginend-global-mode))

5.8 Folding within a subtree

(bind-key "C-c C-h"
          (defun my/org-fold-current-subtree-anywhere-in-it ()
            (interactive)
            (save-excursion (save-restriction
                              (org-narrow-to-subtree)
                              (org-shifttab)
                              (widen))))
          org-mode-map)

5.9 Draw pretty unicode tables in org-mode

This turns the “—” and other ASCII for tables into ‘smooth’ lines ^_^
ab
12
(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.

NOTE. This is comparable to org-modern-mode’s pretty table rendering.

5.10 Drag Stuff

;; Move current word ←/→, or current line ↑/↓.
;; Todo: Compare with org-metaup and org-metadown...
(use-package drag-stuff

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

5.11 Buffer default mode is org-mode

  • I’d much rather have my new buffers in org-mode than fundamental-mode:
(setq-default major-mode 'org-mode)

5.12 Use Org Mode links in other modes: Links can be opened and edited like in Org Mode.

;; E.g., in ELisp mode, the following is clickable and looks nice: [[info:man][Read the docs!]]
;;
;; In particular, when I tangle my init.org into a Lisp file, init.el, it has Org links
;; back to the original source section in Org, which I can then click to jump to, quickly.
;;
(use-package orglink
  :config
  (global-orglink-mode)
  ;; Only enable this in Emacs Lisp mode, for now.
  (setq orglink-activate-in-modes '(emacs-lisp-mode)))

5.13 ox-pandoc

5.14 No code evaluation upon export

;; Ignore all header arguments relating to “:eval”. Do not evaluate code when I export to HTML or LaTeX or anything else.
(setq org-export-use-babel nil)

6 Staying Sane

See The Communicative Value of Using Git Well

Note: Emacs has built-in version control, e.g., kbd:C-x_v_l to see the change log of the repository containing the current file.

6.1 Undo-tree: Very Local Version Control

doc:undo-tree-visualize, kbd:C-x_u, gives a visual representation of the current buffer’s edit history.
<<undo-tree-setup>>

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

6.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 nor bother confirming killing processes.

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

6.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 doc: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 doc:diff-backup for comparing a file with its backup.

#

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

6.3 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/")

6.4   magit —Emacs’ porcelain interface to git

6.4.1 Intro

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
  :bind (("C-c M-g" . magit-file-dispatch))
  :config (global-set-key (kbd "C-x g") 'magit-status)
  :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.

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

    For ease, above, we have also bound this to kbd:C-c_g —reminiscent of kbd:C-x_g 😄

    ;; 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, doc:magit-blame, is super nice: The buffer gets annotations for each chunk of text, regarding who authoured it, when, and their commit title. Then kbd:q to quit the blame.

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

(progn (eshell-command "echo change-here >> ~/dotfiles/.emacs")
       (find-file "~/dotfiles/.emacs"))

6.4.2 Credentials: I am who I am

First, let’s setup our git credentials.
;; Only set these creds up if there is no Git email set up ---ie at work I have an email set up, so don't
;; override it with my personal creds.
;;
;; 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)))

If we ever need to use Git in the terminal, it should be done with Emacs as the underlying editor

;; We want to reuse an existing Emacs process from the command line
;; E.g.,  emacsclient --eval '(+ 1 2)'    # ⇒ 3
(server-start)

;; Or use it whenever we are editing a git message from the terminal
(shell-command "git config --global core.editor 'emacsclient -t -a=\\\"\\\"'")

This will start a daemon if there is not already one running —The -a option— and opens a new Emacs frame on the current terminal —The -t option.

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

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

This doc: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 kbd:C-x_C-e —doc: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 doc:magit-list-repositories: It marks modified repos as “red:dirty”.

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

6.4.5 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 kbd:M-x_git-timemachine on a file and use kbd:p/n/g/q to look at previous, next, goto arbitrary historic versions, or quit.

(use-package git-timemachine )

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


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

6.4.6 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!"))

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

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.

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

(defun add-watchwords () "Add TODO: words to font-lock keywords."
  (font-lock-add-keywords nil
                          '(("\\(\\<TODO\\|\\<FIXME\\|\\<HACK\\|@.+\\):" 1
                             font-lock-warning-face t))))

(add-hook 'prog-mode-hook #'add-watchwords)

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 purple:COMMENT-ed out in org-mode.
  • Ensure you exclude generated files, such as the Emacs backups directory, from being consulted. Using magit, press kbd:i to mark items to be ignored.
  • This feature also works outside of git repos.

Open a Magit status buffer, or run doc: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.

#

6.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 kbd:C-x_g_l_l (doc:magit-log-head) or doc: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 doc:magit-blame-addition or doc:vc-annotate. If we want to “walk-along changes” then we use doc:git-timemachine.

6.8 Git gutter: Jumping between hunks

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

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

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

6.9 URLs to git changes

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 )

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

6.10 Version Control with SVN —Using Magit!

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

6.11 Github within Emacs

We can work with Github/Gitlab/etc from the comfort of Emacs Magit using the forge package.

  • In particular, it adds two new “topics” to the Magit status page: Issues and Pull Requests.
    • We can tab on these to see their “posts” —the contributions to the conversations and new commits.
    • Here is a list of actions that can be done on topics and posts.
(use-package forge
  :after magit)
  1. Make a file ~/.authinfo whose top-most line is machine api.github.com login ⟪𝑼𝑺𝑬𝑹𝑵𝑨𝑴𝑬⟫^forge password ⟪𝑻𝑶𝑲𝑬𝑵⟫ where ⟪⋯⟫ should be replaced by your Github username and a token —which can be created from this page with permissions for repo, user, read:org checked-off.
  2. Now open a Magit status buffer for a repository and run M-x forge-pull, after which you should be able to see all issues and pull requests.
    • Alternatively, press N to see the forge menu.
    • Press N f f to pull latest information from the repository.
      • Note how pulling data from a forge’s API works the same way as pulling Git data does; you do it explicitly when you want to see the work done by others.
      • We can also “c”reate a new “p”ull request or a new “i”ssue with N c p or N c i, respectively.
      • ~5 min video

To review a pull request press tab on Pull Requests then press b y to checkout that branch locally.

7 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 —doc: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))

7.1 Emojis

(if (member "Apple Color Emoji" (font-family-list))
    (set-fontset-font t 'unicode "Apple Color Emoji" nil 'prepend)
  (message-box "Musa: Install the font!"))
;; E.g., Download font such as https://fonts.google.com/noto/specimen/Noto+Color+Emoji
;; Double-click on the ttf file then select “install” to have it installed on your system
;; (Note: Noto does not work on my personal machine.)


;; Render ASCII such as “ :-) ” as emoji 🙂.
(use-package emojify)
(setq emojify-display-style 'unicode) ;; unicode is the way to go!
(setq emojify-emoji-styles '(unicode))
(global-emojify-mode 1) ;; Will install missing images, if need be.

7.2 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
(format "; Time %.3fs"
        (float-time (time-subtract (current-time) before-init-time)))

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 (•̀ᴗ•́)و"))

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

;; I have symlinks for various things, just follow them, do not ask me.
(setq vc-follow-symlinks t)

;; After my settings have been loaded, e.g., fancy priorities
;; and cosmetics, then open my notes files.
(add-hook 'emacs-startup-hook
          (lambda ()
            (-let [my-life.el (getenv "MY_LIFE_ELISP")]
              (unless org-default-notes-file
                (error "Add to .zshrc “ export MY_LIFE_ELISP=\"/full/path/to/my-life.el\" ”, then load my-life.el"))
              (load-file my-life.el))))

There is the neat-looking emacs-dashboard package that provides an extensbile yet minimalist splash screen showing recent files, projects, and bookmarks.

7.4 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 doc:doom-modeline-def-modeline and doc:doom-modeline-set-modeline.

NOTE: We need to run M-x doc:nerd-icons-install-fonts for doom-modeline to use pretty icons —then restart Emacs.

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

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

Spaceline

;; When using helm & info & default, mode line looks prettier.
(use-package spaceline
  :custom (spaceline-buffer-encoding-abbrev-p nil)
          ;; Use an arrow to seperate modeline information
          (powerline-default-separator 'arrow)
          ;; Show “line-number : column-number” in modeline.
          (spaceline-line-column-p t)
          ;; Use two colours to indicate whether a buffer is modified or not.
          (spaceline-highlight-face-func 'spaceline-highlight-face-modified)
  :config (custom-set-faces '(spaceline-unmodified ((t (:foreground "black" :background "gold")))))
          (custom-set-faces '(spaceline-modified   ((t (:foreground "black" :background "cyan")))))
          (require 'spaceline-config)
          (spaceline-helm-mode)
          (spaceline-info-mode)
          (spaceline-emacs-theme))

(spaceline-toggle-buffer-size-off) ;; Not interested in how large a buffer is.
(spaceline-toggle-input-method-off) ;; Usually a “Π” symbol in the modeline, since I use Unicode input with Agda, denoting the current language environment, buffer coding system, and current input method.
(spaceline-toggle-buffer-encoding-abbrev-off) ;; buffer-encoding-abbrev: the line ending convention used in the current buffer (unix, dos or mac).
(spaceline-toggle-hud-off) ;; A tiny “progress bar”; shows the currently visible part of the buffer.
(spaceline-toggle-helm-buffer-id-off) ;; I don't need to see “Helm M-x” whenever I press “M-x” or other Helm-powered commands.

Other separators —of modeline information— that I’ve considered include 'brace instead of an arrow, and 'contour, 'chamfer, 'wave, 'zigzag which look like browser tabs that are curved, boxed, wavy, or in the style of driftwood.

7.4.1 Menu to Toggle Minor Modes: A quick way to see all of my modes, and which are enabled

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. ⚙. ⚙️

(setq doom-modeline-minor-modes t)
(use-package minions

  :init (minions-mode 1))

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

7.4.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 doc: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)

7.4.4 No Line, nor Column, Numbers —and no buffer percentage

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.

;; ;; Do not show me line numbers, nor column numbers, in the modeline
(column-number-mode -1)
(line-number-mode   -1)

;; Likewise, no need to show me “Top∣Mid∣Bot” in the modeline.
(setq-default mode-line-percent-position nil)

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 kbd:M-g_g; and toggle line numbers on when I’m pair programming with doc: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)

7.4.5 Buffer names are necessarily injective

By default when multiple files sharing the same name are opened, say for comparison from different directories, their buffers are named uniquely by having the format “⟨file-name⟩ <𝓃>”, for numbers 𝓃. It’d be more helpful to have the buffer names reflect their location.

;; Note that ‘uniquify’ is builtin.
(require 'uniquify)
(setq uniquify-separator "/"               ;; The separator in buffer names.
      uniquify-buffer-name-style 'forward) ;; names/in/this/style

Note that this does not affect cloning buffers, kbd:C-x_4_c.

( A function f is injective precisely when it’s distinction-preserving; i.e., x ≠ y ≡ f x ≠ f y. We can tell whether two things are the same or not, by ‘zooming in’ on their particular property ‘f’, which may be easier to compare. E.g., object IDs, hashcodes, unique keys in database tables. )

( Why am I bringing this up? I like math and seldom get to use it; so why not! )

7.5 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 kbd:C-c_t, doc:my/toggle-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
      (cl-loop for (package . theme-variants-I-like) in
               ;; I like theme doom-flatwhite <3 It feels “warm”.
               ;; (I found out thanks to C-u C-c t!)
               '((doom-themes doom-flatwhite doom-snazzy doom-monokai-ristretto doom-laserwave doom-solarized-light doom-vibrant)
                 (solarized-theme solarized-gruvbox-dark solarized-gruvbox-light)
                 (stimmung-themes stimmung-themes-light stimmung-themes-dark)
                 (shanty-themes shanty-themes-light)
                 (apropospriate-theme apropospriate-light) ;; /super/ nice! Super “clean”, like writing on paper
                 (tao-theme tao-yang) ;; nice light theme.
                 (leuven-theme leuven-dark leuven) ;; Nice minimal variant
                 (material-theme material-light)
                 (moe-theme moe-light)
                 (organic-green-theme organic-green)
                 (tango-plus-theme tango-plus)
                 ;; I like all 3 variants.
                 (minimal-theme minimal minimal-black minimal-light)
                 (espresso-theme espresso)
                 (emacs dichromacy)
                 (modus-themes modus-operandi-tinted))
               do (package-install package)
               append theme-variants-I-like))

(setcdr (last my/themes) my/themes)

kbd:C-c_t to toggle between the personal themes.

(cl-defun my/load-theme (&optional (new-theme (completing-read "Theme: " (custom-available-themes))))
  "Disable all themes and load the given one ---read from user when called interactively."
  (interactive)
  (mapc #'disable-theme custom-enabled-themes)
  (load-theme new-theme)
  (message "Theme %s" new-theme))

(cl-defun my/toggle-theme (&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)
  (-let [theme (if current-prefix-arg
                   (nth (random (length (custom-available-themes)))
                        (custom-available-themes))
                 new-theme)]
    (my/load-theme theme)))

(global-set-key "\C-c\ t" 'my/toggle-theme)

(my/toggle-theme)

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, kbd:C-c_F, doc:my/toggle-font, to quickly change fonts (according to mood 😸). [I already use kbd:C-c_f, doc:my/org-mode-format, for the more likely operation of formatting text.]

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
  ;; Breaks Gerrit: (my/toggle-font "Roboto Mono Light 14")
  (my/toggle-theme 'solarized-gruvbox-light))

7.6 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
  :config (setq beacon-color "#666600")
  :hook   ((org-mode text-mode) . beacon-mode))

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

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

7.9 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

7.10 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
  :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>")
;; Act as usual unless a ‘<’ or ‘>’ is encountered.
;; ( char-at is really “character at poisition”; C-h o! )
(setq rainbow-delimiters-pick-face-function
      (lambda (depth match loc)
        (unless (member (char-after loc) '(?< ?>))
          (rainbow-delimiters-default-pick-face depth match loc))))

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.

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

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

(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
                       ("i32" . ?ℤ) ("u32" . ?ℕ) ("f64" . ?ℝ) ;; Rust specific
                       ("bool" . ?𝔹)
                       ;; ("\"\"\"\n" . ?“) ("\"\"\"" . ?”)
                       ("\"\"\"" . ?“)
                       ("fn" . )
                       ("<=" . ?≤) (">=" . ?≥)
                       ("->" . ?→) ("-->". ?⟶) ;; 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 fileimage filetags)
         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)))))


(add-hook 'org-mode-hook (lambda () (push '("# " . (?🎶 (Br . Bl) ?\ )) prettify-symbols-alist)))

For more on prettify-symbols-mode, read the informative Prettifying LaTeX Buffers.

(<= (+ 1 1) (--> 2))

1 =
2 ==
3 ===

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)

7.13 Hiding Emphasise Markers, Inlining Images, and LaTeX-as-PNG

$e^x = ∑n = 0^∞ \frac{x^n}{n!}$

awkward or $not$

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 ⋅ π} - 1 = 0$ or $\substack{𝔹 \ ↓ \ 𝒜}$. These can be toggled with kbd: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
  :disabled t
  :hook (org-mode . org-fragtog-mode))

doc:org-latex-preview, kbd:C-c_C-x_C-l, renders $e^{i \pi} + 1 = 0$ into a really nice inline image: $ei π + 1 = 0$. It also works for LaTeX environments —for personal environments, just (add-to-list 'org-latex-packages-alist "LaTeX definitions here").

[Disabled]

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

;; \step should be local to \begin{calc}⋯\end{calc}!
(add-to-list 'org-latex-packages-alist
"\\def\\BEGINstep{ \\{ }
\\def\\ENDstep{ \\} }
\\newcommand{\\step}[2][=]{ \\\\ #1 \\;\\; & \\qquad \\color{maroon}{\\BEGINstep \\text{ #2 } \\ENDstep} \\\\ & }
\\newenvironment{calc}{\\begin{align*} & }{\\end{align*}}")

; (pop org-latex-packages-alist)

;; See also org-format-latex-header
;; Support “latex-as-png” src blocks, which show LaTeX as PNGs
(use-package ob-latex-as-png :disabled t)
  

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)

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

7.15 Powerful Directory Editing with dired :Disabled:

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…

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

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.

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

7.17 Tabs

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

7.18 Window resizing using the golden ratio

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
  :init (golden-ratio-mode 1))

After some time this got a bit annoying and I’m no longer using this.

An alternative, also disabled:

;; An automatic window-resizing mechanism.
;; A “calmer” alternative to golden-ratio.
;; https://github.com/cyrus-and/zoom
(use-package zoom :config (zoom-mode t))

7.19 Org-Emphasise for Parts of Words

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.

7.20 Preview link under cursor

When cursor sits on a URL/Image/File link, try to preview it in a tooltip.
(quelpa '(preview-it :repo "jcs-elpa/preview-it" :fetcher github))
;; (global-preview-it-mode)

This also works nicely when I’m looking to open a file; e.g., C-x C-f ~/.emacs.d/.as TAB to preview my .aspell.en.pws (Emacs personal dictionary) file in a tooltip.

Likewise, I’d like to preview line when executing the doc:goto-line / kbd:M-g_M-g command.

(quelpa '(goto-line-preview :repo "jcs-elpa/goto-line-preview" :fetcher github))
(global-set-key [remap goto-line] 'goto-line-preview)

7.21 Replace phrases with nice SVG labels

Some org headings are not fontified, and Org links are not fontified; e.g.,

📆 Calendar ✉️

  • I have to copy-paste it to get it to fontify.
  • Likewise, Org headings need to be modified (e.g. pressing `t` to make them todo) to get them fontified.

SVG tags mode 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 doc: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.
(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.22 Now C-c C-x C-v shows remote images inline, neato!

(quelpa '(org-remoteimg :fetcher github :repo "gaoDean/org-remoteimg"))
(require 'org-remoteimg)
(setq url-cache-directory "~/emacs.d/.cache/")
(setq org-display-remote-inline-images 'cache)

Example: lexing vs parsing

1% better everyday, push-ups!

7.23 Ibuffer: C-x C-b shows colour-coded buffers, grouped according to their git repos

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

7.25 all-the-icons

(use-package all-the-icons
    :config (all-the-icons-install-fonts 'install-without-asking))

7.26 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
  :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 😃

7.27 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)
  (ignore-errors (mapcar #'kill-buffer (--filter (s-matches? "\\*.*\\*" it) (mapcar #'buffer-name (buffer-list))))))

7.28 [#A] COMMENT When I press C-x ←/→ I’d like to ignore some silly buffers.

  • If I want to see a particular buffer, I’ll summon it explicitly.
    • E.g., Messages with C-h e, Magit with C-x g, Server Status with M-S-SPC, etc.
(defun my/buffer-predicate (buffer)
  "Run `C-u 0 C-x C-e' on the following form to see all buffer names and find the
   ones annyoning you, then place those in the function body below

        (mapcar #'buffer-name (buffer-list))
"
  ;; First let's kill a bunch of buffers
  ;; (my/clean-buffers) ;; TODO: Bad idea?

  ;; Next let's filter out any remaining ones [Redundant?]
  (defvar my/ignore/buffer/name '("*Quail Completions*" "*Backtrace*" "*Help*" "*agda2*" "*sqls*" "*which-key*" "*Warnings*" "*Messages*" "Status of Services"))
  (defvar my/ignore/buffer/prefix '("*helm" "*Helm"  "*quelpa" "*lsp" "*Occur" "magit" "*Flymake" "*format" "*Shell" "*Async"
                                    "*org-src-fontification:" "*Server:"))
  (defvar my/ignore/buffer/suffix  '("stderr*" "log*" "-ls*"))

  (-let [name (buffer-name buffer)]
    (not (or (member name my/ignore/buffer/name)
             (--any? (s-starts-with? it name) my/ignore/buffer/prefix)
             (--any? (s-ends-with? it name) my/ignore/buffer/suffix)))))

(set-frame-parameter nil 'buffer-predicate 'my/buffer-predicate)

Note: C-x C-r is very useful for jumping to recently visited files.

8 Lisp Programming

8.1 C-x C-e ∷ REPL-driven development for ELisp

Evaluate code and see the results inline —A feedback loop that is faster than ever!

Within Emacs, kbd: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 https://alhassy.com/repl-driven-development, including setup for other languages.

8.1.1 ELisp

By default, Emacs Lisp’s kbd: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.

  • kbd:C-u_C-x_C-e inserts the evaluation result at point; kbd:C-u_0_C-x_C-e does so without truncating lengthy output.
  • Read this Sweet & short blog/GIFs on practical uses of kbd:C-x_C-e when working with Lisp.
;; Evaluation Result OverlayS for Emacs Lisp
(use-package eros :init (eros-mode t))

8.2 RDD

8.2.1 get the pkg

(use-package repl-driven-development)
;; TODO: rdd.el should (require ⋯) the following!
(use-package json-navigator)
(use-package ansi-color)

(setq repl-driven-development-echo-duration 10)

8.2.2 terminal

;; Sometimes I see a bunch of shell incantations in a README or something and I'd like to execute them right there and then,
;; and not have to bother with copying them over to a terminal and execute there. As such, here's a quick key binding to execute
;; shell commands from anywhere.
;; (repl-driven-development [C-x C-t] "bash"  :prompt "bash-3.2\\$")
(repl-driven-development [C-x C-t] terminal)

8.2.3 jshell

;; TODO: Changes to be pushed to master rdd.el ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; new
(defun repl-driven-development--main-callback (repl)
  "Return the callback that works on REPL."
  `(lambda (process output)
     ;; This is done to provide a richer, friendlier, interaction.
     ;; ^M at the end of line in Emacs is indicating a carriage return (\r)
     ;; followed by a line feed (\n).
     (setq output (repl-driven-development--ignore-ansi-color-codes output)) ;; ⟵ ⟵ Main change here ⟵ ⟵  ⟵ ⟵ ⟵ ⟵ ⟵ ⟵
     ;; (message-box "%s" output)
     (setq output (s-trim (s-replace-regexp ,(rdd@ repl prompt) ""
                                      (s-replace "\r\n" "" output))))
     ;; Output is always non-empty
     (unless (s-blank? (s-trim output))
       (setf (rdd@ (quote ,repl) output) output))
     (repl-driven-development--insert-or-echo (quote ,repl) output)))

;; Notice that I've changed :prompt!
(defun repl-driven-development-preconfiguration:java (keys)
  "<omitted>"
  (repl-driven-development
   keys
   ;; enable assertions, and add everything installed, via `mvn', in scope.
   (format "jshell --class-path %s --enable-preview -R -ea --feedback silent"
    (concat ".:" (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f 2>/dev/null | tr '\n' ':'")))
   :name 'java-eval
   :prompt "[^\\n]*=>\\.\\.\\.\\." ;; ⟵ ⟵ Main change here ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵
   :input-rewrite-fn
   #'repl-driven-development--strip-out-C-style-comments&newlines
   :init "/set mode EmacsJavaMode normal -command
/set format EmacsJavaMode display \"{pre}added import {name}{post}\" import-added
/set format EmacsJavaMode display \"{pre}re-added import {name}{post}\" import-modified,replaced
/set format EmacsJavaMode result \"{type} {name} = {value}{post}\" added,modified,replaced-primary-ok
/set truncation EmacsJavaMode 40000
/set feedback EmacsJavaMode
import javax.swing.*;
System.out.println(\"Enjoy Java with Emacs (。◕‿◕。))\")")
  (defalias 'java-eval-read #'repl-driven-development--java-read))

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

;; Set “C­x C­j” to evaluate Java code in a background REPL.
(repl-driven-development [C-x C-j] java)

8.3 ⌘-e: Edit Everything in a separate buffer

Edit comment/string/docstring/code block in separate buffer with your favourite mode.

  • ⌘-e to toggle “e”diting of thing at point.
  • ⌘-e will try to edit thing at point, or selection, if possible; otherwise it will exit edit session.
    • 🔥 Avoid escape nightmares by editing strings in a separate buffer.
    • On a string, press ⌘-e and a new buffer pops up with unescaped content, letting you edit raw strings directly. It then takes care of automatically escaping strings for you when you press ⌘-e, or C-c C-c —or discard your changes with C-c C-k.
  • ⌘-e can be used within edit session to create new edit sessions, if possible; then ⌘-e will “pop-off-the-stack” as expected.
    • (I already use ⌘-e to toggle editing Org src blocks, and when editing a block ⌘-e will try to do a recursive edit if possible, or exit the edit session.)
    • (The separedit package also let’s me press ⌘-e to edit variable values when describing them, and to edit text when in the minibuffer.)

From source:

+----------+         Edit           +-----------+         Edit           +-----------+
|          | ---------------------> |   edit    | ---------------------> |   edit    | ...
|          |  point-at-comment?     |   buffer  |  point-at-comment?     |   buffer  |
|  source  |  point-at-string?      |           |  point-at-string?      |           | ...
|  buffer  |  point-at-codeblock?   | (markdown |  point-at-codeblock?   | (markdown | ...
|          |  point-at-...?         |  orgmode  |  point-at-...?         |  orgmode  |
|          | <--------------------- |   ...)    | <--------------------- |   ...)    | ...
+----------+     Commit changes     +-----------+     Commit changes     +-----------+

[In-general, we can invoke M-x edit-indirect-region to edit any selected piece of text in its own buffer, then C-c C-c (or ⌘-e with my setup) to commit the edit. Actually, I’ve altered by ⌘-e setup to also account for a selected region 😉]

(use-package separedit)
;;
;; # Example Usage
;;
;; 1. Press ⌘-e on this line, to edit this entire comment.
;; 2. Press ⌘-e to exit the edit session.
;;
;; Since my ⌘-e is context sensitive, to determine whether to continue editing or
;; exit; you can explicitly request an edit with C-c ' and an exit with C-c C-c.
;;
;; ```
;; ;; 3. Press ⌘-e on this line, to edit this source block!
;; ;; 4. Press ⌘-e on this line, to edit this inner-most comment!
;; ;; 5. At start of next line, press “⌘-r ⌘-e” to edit just the source block
;; ;;
;; (cl-defun index (&rest args)
;;   "6. Press ⌘-e to edit this string, \"7. and again in these quotes\""
;;   "<p>8. Press ⌘-e to edit this <strong> HTML </strong> block, in Web-mode </p>")
;;
;; ;; 9. Press C-u ⌘-e to guess the language of the next string (Rust); then ⌘-r C-c C-r to quickly run the code.
;; "fn main() { println!(\"{}\", \"hello!\"); }"
;;
;; ;; 10. Select & press “C-u ⌘-e” on the following, to edit it in whatever mode you want.
;; ;; select * from table -- Or just press ⌘-e and have the mode detected.
;;
;; ```

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Setup to make the above ⌘-e behaviour happen.

;; Make “⌘-e” toggle editing string literals / select region / [Org/markdown] code block / comment block when programming.
(--map (bind-key "s-e" #'separedit it)
       '(prog-mode-map minibuffer-local-map help-mode-map)) ;; TODO: helpful-mode-map
;; TODO:Merge these changes upstream

;; I'm focusing on a specific region to edit, so let's not be distracted by anything else.
;; This makes the “editing stack” feel like a stack, with ⌘-e pushing new editing session buffers,
;; and C-c C-c, or ⌘-e on non-editable lines, to pop-off the stack.
;; (advice-add #'separedit :after (lambda (&rest _) (delete-other-windows)))
;;
;; NOTE: This actually breaks the stack nature of popping with ⌘-e; we need to actually save the stack via some list of buffers than push/pop buffers on that variable.

;; I don't want to be bothered for what mode I'm in, when a region is selected using current major mode.
;; I'll use a prefix, “C-u ⌘-e”, if I want to select a mode for my current selected text.
(advice-add #'separedit--select-mode :before-until
            (lambda (&rest _)
              (when (and (not current-prefix-arg) (region-active-p)) (pp-to-string major-mode))))

;; Also: When on a string ∷
(advice-add #'separedit--select-mode :before-until
            (lambda (&rest _)
              "When on a string ∷
+ ⌘-e ⇒ Edit string at point
+ C-u ⌘-e ⇒ Auto-detect my string's major mode
+ C-u C-u ⌘-e ⇒ Let me select a major mode"
              (-let [str? (ignore-errors (thing-at-point 'string))]
                (case (car current-prefix-arg)
                  (4 (when str? (pp-to-string (my/detect-prog-mode str?))))
                  (_ nil)))))

;; NOTE: By default, separedit provides colouring for 'strings', "strings", and `strings'
;; This doesn't look very good when I have a single quote within double quotes:
;; In an Emacs Lisp buffer, editing the string "Bob's Work" gives unexpected highlighting.
;; ```
;; (advice-add #'separedit :after
;;             (lambda (&rest _)
;;               (when (s-ends-with? "string-mode" (pp-to-string major-mode))
;;                 (text-mode))))
;; ```
;; In the indirect buffer, make ⌘-e finish editing.
(use-package edit-indirect
  :config (bind-key "s-e"
                    (lambda ()
                      (interactive)
                      (or (ignore-errors (call-interactively #'separedit))
                          (call-interactively #'edit-indirect-commit)))
                    #'edit-indirect-mode-map))

;; I also have “s-e” bound to `org-edit-src-exit'.
(advice-add 'org-edit-src-exit :before-until
            (lambda (&rest r)
              (when (ignore-errors (separedit)) t)))
;; → ⌘-e on an Org paragraph pops-up an edit session in Org mode.
;; → ⌘-e on a selection in Org mode pops-up an edit session in Org mode.
;; TODO: Consider forming an alist for special blocks to refer to their preferred
;; edit mode, defaulting to Org-mode? Perhaps something to consider /after/
;; addressing the bug below.
;; (advice-unadvice 'org-edit-special) MA: TODO: FIXME: Delete this?
(advice-add 'org-edit-special :around
            (lambda (orginal-function &rest r)
              (cond
               ((region-active-p) (call-interactively #'edit-indirect-region) (org-mode))
               ((equal 'paragraph (car (org-element-at-point)))
                (mark-paragraph) (call-interactively #'edit-indirect-region) (org-mode))
               (t (or (ignore-errors (apply orginal-function r))
                      ;; We try to edit a special block when orginal-function fails.
                      ;; This way src blocks are not confused with the more generic idea of special blocks.
                      (when
                          (my/org-in-any-block-p)
                        ;; Note using org-element-at-point doesn't work well with special blocks when you're somewhere within the block.
                        ;; It only works correctly when you're on the boundary of the special block; which is not ideal.
                        ;; This is why I'm not using: (org-element-property :begin elem).
                        (-let [(start . end) (my/org-in-any-block-p)]
                          (set-mark-command start)
                          (goto-char end) (previous-line 2) (end-of-line) ;; FIXME: Still shows #+end_XXX for some reason.
                          (call-interactively #'edit-indirect-region) (org-mode))))))))
;; where...
(defun my/org-in-any-block-p ()
  "Return non-nil if the point is in any Org block.

The Org block can be *any*: src, example, verse, etc., even any
Org Special block.

This function is heavily adapted from `org-between-regexps-p'.

Src: https://scripter.co/splitting-an-org-block-into-two/"
  (save-match-data
    (let ((pos (point))
          (case-fold-search t)
          (block-begin-re "^[[:blank:]]*#\\+begin_\\(?1:.+?\\)\\(?: .*\\)*$")
          (limit-up (save-excursion (outline-previous-heading)))
          (limit-down (save-excursion (outline-next-heading)))
          beg end)
      (save-excursion
        ;; Point is on a block when on BLOCK-BEGIN-RE or if
        ;; BLOCK-BEGIN-RE can be found before it...
        (and (or (org-in-regexp block-begin-re)
                 (re-search-backward block-begin-re limit-up :noerror))
             (setq beg (match-beginning 0))
             ;; ... and BLOCK-END-RE after it...
             (let ((block-end-re (concat "^[[:blank:]]*#\\+end_"
                                         (match-string-no-properties 1)
                                         "\\( .*\\)*$")))
               (goto-char (match-end 0))
               (re-search-forward block-end-re limit-down :noerror))
             (> (setq end (match-end 0)) pos)
             ;; ... without another BLOCK-BEGIN-RE in-between.
             (goto-char (match-beginning 0))
             (not (re-search-backward block-begin-re (1+ beg) :noerror))
             ;; Return value.
             (cons beg end))))))

I’d like to guess the language I’m in, when working with strings.

(use-package language-detection)
;; Usage: M-x language-detection-buffer ⇒ Get programming language of current buffer
;; Also, (language-detection-string "select * from t") ;; ⇒ sql

;; TODO: Push this upstream; https://github.com/andreasjansson/language-detection.el/issues/1
(cl-defun my/detect-prog-mode (&optional string)
  "Guess programming mode of the current buffer, or STRING if it is provided.

When called interactively, it enables the mode;
from Lisp it just returns the name of the associated mode.

    ;; Example Lisp usage
    (call-interactively #'my/detect-prog-mode)

`language-detection-buffer' returns a string which is not always the name of the
associated major mode; that's what we aim to do here."
  (interactive)

  (defvar my/detect-prog-mode/special-names
    '((c           . c-mode)
      (cpp         . c++-mode)
      (emacslisp   . emacs-lisp-mode)
      (html        . web-mode) ;; I intentionally want to use this alternative.
      (matlab      . octave-mode)
      (shell       . shell-script-mode)
      (visualbasic . visual-basic-mode)
      (xml         . sgml-mode))
    "Names in this alist map a language to its mode; all other languages 𝒳 have mode ‘𝒳-mode’ afaik.")

  (let* ((lang (if string (language-detection-string string) (language-detection-buffer)))
         (mode (or (cdr (assoc lang my/detect-prog-mode/special-names))
                   (intern (format "%s-mode" lang)))))
    (if (called-interactively-p 'any)
        (progn (call-interactively mode) (message "%s enabled!" mode))
      mode)))
  • ⌘-e to edit an Org table cell; see here for built-in table editing commands.
(advice-add #'org-edit-special :before-until
            (lambda (&rest r)
              (when (equal 'table-row (car (org-element-at-point)))
                (call-interactively #'org-table-edit-field))))

8.4 ⌘-r, ⌘-i, ⌘-o: Sleek Semantic Selection

Super sleek way to select regions: Anywhere press kbd:⌘-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.4.1 Semantic Change

Using kbd:⌘-i and kbd:⌘-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

  :bind (("s-i" . #'change-inner)
         ("s-o" . #'change-outer)))

8.5 Documentation

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

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

If you query C-h o mapcar you get some nice docs, but it’d be even nice to get some example usage of that method.

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

8.5.3 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

8.5.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 doc : 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.5 Elisp live 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.

(use-package eldoc
  :hook (emacs-lisp-mode . turn-on-eldoc-mode))

;; Slightly shorten eldoc display delay.
(setq eldoc-idle-delay 0.4) ;; Default 0.5

8.6 Jumping to definitions & references

Friendly reminder that M-. takes you to the definition, and M-, takes you back to where you originally where.

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.6.1 An “auto read only” detection mechanism —when jumping to definitions

Files in 3ʳᵈ-party directories should be read-only whenever I open them, when I’m jumping to definitions (with M-.).

;; Usage: Press “M-”. “use-package” below and you can accidentally alter the source code!
;; But in this case you likely just wanted to see the 3ʳᵈ-party definition, not alter it.
;; As such, with this advice, the source will not be alterable (unless you toggle read-only mode).
(advice-add #'xref-find-definitions :after
            (lambda (&rest _)
              (when (--map (s-ends-with? it (f-parent buffer-file-name))
                           '("lisp/emacs-lisp" "/lisp" ".emacs.d/elpa/"))
                (read-only-mode))))

8.7 Comments

8.7.1 Commenting

Let’s get some nifty commenting features —the link has nice usage gifs.

  • M-; 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.

    C-u_M-; indents the current enf-of-line comment with any above it.

    For use with Org-mode, it’s best to use doc:org-edit-src-code —which I’ve bound to ⌘ e.

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

8.7.2 Emphasised Comments: Useful for warnings

;; In VSCode, with the “Better Comments” extension, comments starting with a “bang” are made to stand out, via bold red.
;; Let's do the same thing in Emacs.
;; I did not look around, there might be a package/option for this 🤷
(add-hook 'prog-mode-hook
          (defun emphasize-comments-starting-with-! ()
            (highlight-lines-matching-regexp ".*\\*.*!.*" 'hi-red-b)
            (highlight-lines-matching-regexp ".*//!.*" 'hi-red-b)
            (highlight-lines-matching-regexp ";;!.*" 'hi-red-b))) ;;! Look it works (。◕‿◕。)

8.7.3 Comment-boxes up to the fill-column —or banner instead?

GIF: https://endlessparentheses.com/images/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)))

A comment box sometimes increases the size of a file more than I’d like, or more than others on my dev team would like. As such, let’s try banner comments:

(use-package banner-comment)

Pretty slick!

8.8 Text Folding —Selectively displaying portions of a program

⟦‼ TODO: hideshowvis shows "*hideshowvis*" literally in src code blocks upon export; as such we need to hook to exports so as to temporarily disable it. ⟧

(require 'cl-lib)

(defun my/disable-hs-hide-all (orig-fun &rest args)
  "Advise `org-export-dispatch` to disable `hs-hide-all` temporarily."
  ;; Without this, export hangs “Hiding all blocks...”
  (cl-letf (((symbol-function 'hs-hide-all) (lambda (&rest _) nil)))
    ;; Without this, export shows “*hideshowvis*” markers in my exported code blocks.
    (cl-letf (((symbol-function 'hideshowvis-highlight-hs-regions-in-fringe) (lambda (&rest _) nil)))
      (apply orig-fun args))))

(advice-add 'org-export-dispatch :around #'my/disable-hs-hide-all)

Literate programming within Org-mode is not always ideal or possible, so we use a programming mode directly and then may want to have arbitrary ‘sections’ of text folded up. Let’s describe how to accomplish this goal. I’ve tried the feature-full folding modes Origami-mode and yafolding-mode, but it seems the built-in TODO. doc.hs-minor-mode is the best: (0) It folds comments, (1) it’s the fastest, and (2) it Just Worksᵀᴹ out-of-the-box. Coupled with Vimish folding, for arbitrary region folding; and a helpful hydra, I’m rather happy with my setup.

On two JavaScript files, I found the following timings (using TODO. doc.elp-instrument-function and TODO. doc.elp-results):

Folding function~500 lines~2300 lines
hs-hide-all0.002770.004358
origami-close-all-nodes0.003040.025569
yafolding-hide-all7.05179574.252598

(TODO: Maybe these timings are due to my config, I should try to reproduce these with emacs -Q.)

8.8.1 Actual Setup


TODO: Below I setup hideshow along with [doc : hideshowvis-symbols] [hideshowvis] which gives us a nice clickable “+/-” marker for foldable blocks as well as an overlay that indicates how many lines are folded away. For more on hideshow-mode, see the Hidden Gem Packages presentation which contains a small self-contained introduction, or these super useful source comments which discuss hooks and support for special modes (like this one).

(use-package hideshow
  :init
  ;; https://github.com/emacsmirror/emacswiki.org/blob/master/hideshowvis.el
  (quelpa '(hideshowvis :fetcher wiki))

  ;; Press “C-c TAB” to toggle a block's visibility or “C-c f” for my folding hydra.
  :bind (("C-c TAB" . hs-toggle-hiding))

  ;; https://github.com/shanecelis/hideshow-org/tree/master
  ;; This extension bring Org-mode tab behaviour to folding, at the block level
  ;; and buffer level ---but not cycling visibility.
  ;; (use-package hideshow-org) ;; Disabled as commented below.

  :hook ((prog-mode . (lambda () (hs-minor-mode +1)
                        (hideshowvis-minor-mode t)
                        (hideshowvis-symbols)
                        ;; This hook along with hs-org mode breaks editing of src blocks in Org files.
                        ;; That's OK, since my folding hydra does a better job for my needs.
                        ;; (hs-org/minor-mode t)
                        (hs-hide-all)))))

Along with a hydra for super quick navigation and easily folding, unfolding blocks! Love this one (•̀ᴗ•́)و

(my/defhydra "C-c f" "Folding text" archive
  :Current
  ("h" hs-hide-block "Hide")
  ("s" hs-show-block "Show")
  ("t" hs-toggle-hiding "Toggle")
  ;; "l" hs-hide-level "Hide blocks n levels below this block"; TODO: Enable folding feature
  :Buffer
  ("H" hs-hide-all "Hide")
  ("S" hs-show-all "Show")
  ("T" my/hs-toggle-buffer "Toggle")
  :Style
  ("i" my/clever-selective-display "Fold along current indentation" :toggle selective-display)
  ("e" auto-set-selective-display-mode  "Explore; walk and see" :toggle t)
  :Region
  ("f" (lambda () (interactive) (vimish-fold-toggle) (vimish-fold (region-beginning) (region-end))) "Fold/Toggle")
  ("d" vimish-fold-delete "Delete fold")
  ("U" vimish-fold-unfold-all "Unfold all")
  ("D" vimish-fold-delete-all "Delete all")
  ("n" vimish-fold-next-fold "Next fold")
  ("p" vimish-fold-previous-fold "Previous fold")
  :...
  ("w" hl-todo-occur "Show WIPs/TODOs" :exit t)
  ("m" lsp-ui-imenu "Menu of TLIs" :exit t) ;; TLI ≈ Top Level Items
  ;; ("i" imenu-list "iMenu (General)") ;; It seems the above is enough for both prog and otherwise.
  ("r" (progn (hs-minor-mode -1) (hs-minor-mode +1)) "Reset Hideshow")  ;; Remove all folds from the buffer and reset all hideshow-mode. Useful if it messes up!
  ("q" nil "Quit" :color blue))

;; Features from origami/yafolding that maybe I'd like to implement include:
;; narrowing to block or folding everything except block, navigating back and forth between folded blocks.
;; Finally, if we want to cycle the visibility of a block (as in Org-mode), we can use a combination of hs-show-block and hs-hide-level.
(defun my/clever-selective-display (&optional level)
"Fold text indented same of more than the cursor.

This function toggles folding according to the level of
indentation at point. It's convenient not having to specify a
number nor move point to the desired column.
"
  (interactive "P")
  (if (eq selective-display (1+ (current-column)))
      (set-selective-display 0)
    (set-selective-display (or level (1+ (current-column))))))
;; Src: https://emacs.stackexchange.com/questions/52588/dynamically-hide-lines-indented-more-than-current-line
(define-minor-mode auto-set-selective-display-mode
  "Automatically apply `set-selective-display' at all times based on current indentation."
  nil "$" nil
  (if auto-set-selective-display-mode
      (add-hook 'post-command-hook #'auto-set-selective-display nil t)
    (remove-hook 'post-command-hook #'auto-set-selective-display t)
    (with-temp-message ""
      (set-selective-display nil))))
;;
(defun auto-set-selective-display ()
  "Apply `set-selective-display' such that current and next line are visible.

Scroll events are excluded in order to prevent wild flickering while navigating."
  (unless (eq last-command #'mwheel-scroll)
    (let*((this-line-indent (current-indentation))
          (next-line-indent (save-excursion (forward-line) (current-indentation))))
      (with-temp-message "" ; Suppress messages.
        (set-selective-display (1+ (max this-line-indent next-line-indent)))))))

With expected support for searching.

;; Open folded nodes if a search stops there.
(add-hook 'helm-swoop-after-goto-line-action-hook #'my/search-hook-function)
(defun my/search-hook-function ()
  (when hs-minor-mode (set-mark-command nil) (hs-show-block) (pop-to-mark-command)))

With expected support for searching.

(defun my/search-hook-function ()
  (when origami-mode (origami-open-node-recursively (current-buffer) (point))))

;; Open folded nodes if a search stops there.
(add-hook 'helm-swoop-after-goto-line-action-hook #'my/search-hook-function)
;;
;; Likewise for incremental search, isearch, users.
;; (add-hook 'isearch-mode-end-hook #'my/search-hook-function)

And hydra…

(my/defhydra "C-c f" "Folding text" archive
  :Current
  ("h" origami-close-node-recursively "Hide")
  ("s" origami-open-node-recursively  "Show")
  ("c" origami-recursively-toggle-node "Cycle") ;; Acts like org-mode header collapsing. Cycle a fold between open, recursively open, closed.
  ("f" origami-show-only-node "Focus") ;; Close everything but the folds necessary to see the point. Very useful for concentrating on an area of code.
  ;; ("H" origami-close-all-nodes "Hide All")
  ;; ("S" origami-open-all-nodes "Show All")
  :Navigate
  ("n" origami-next-fold "Next")
  ("p" origami-previous-fold "Previous")
  :...
  ("t" origami-toggle-all-nodes  "Toggle buffer")
  ("m" lsp-ui-imenu "Menu of TLIs" :exit t) ;; TLI ≈ Top Level Items
  ;; ("i" imenu-list "iMenu (General)") ;; It seems the above is enough for both prog and otherwise.
  ("r" origami-reset)) ;; Remove all folds from the buffer and reset all origami state. Useful if origami messes up!

8.8.2 E2E Test

8.9 Aggressive Indentation

With a single space or tab, my code should always remain indented.

aggressive-indent-mode is a minor mode that keeps your code always indented. It reindents after every change, making it more reliable than electric-indent-mode.

;; Always stay indented: Automatically have blocks reindented after every change.
(use-package aggressive-indent :config (global-aggressive-indent-mode t))

;; Use 4 spaces in places of tabs when indenting.
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)

This mode requires my Lisp forms to be well-balanced; e.g., having a missing parenthesis does not indent. For instance, I have a form F and I write (when F but have not yet enclosed the final parens, I still want nice indentation. So…

;; This minor mode toggles on along with elisp-mode and indents on the fly when
;; you edit the code. No special key strokes needed.
;; Demo: https://www.youtube.com/watch?v=zrFmfFZfj-A&ab_channel=JiahaoLi
(use-package el-fly-indent-mode
  :config (add-hook 'emacs-lisp-mode-hook #'el-fly-indent-mode))

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


(load-file "~/.emacs.d/elpa/highlight-indent-guides-20200820.2328/highlight-indent-guides.el")

8.9.3 Being Generous with Whitespace

Disabled: I’m not actively writing C-like code in Emacs.

The following minor mode automatically adds spacing around operators.

(use-package electric-operator
  :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.9.4 Auto-format on Save

Disabled: I mainly code in ELisp and so don’t require this utility.

Auto-format source code in many languages with one command

Let’s 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. Better yet, we ask for it to do so “on save”.

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

Prettier is an opinionated code formatter for numerous web related languages, including JS, TS, HTML, CSS, JSON, Vue, Markdown, and YAML.

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

For example, in a NodeJS app make a file .prettierrc.json whose contents are

{
    "singleQuote": true,
    "arrowParens": "avoid",
    "printWidth": 120,
    "semi": false,
    "trailingComma": "none"
}

Unfortunately, my current team prefers "semi": true —which is understandable for people not familar with how JavaScript does semicolon insertion.

( If you’re having trouble getting prettier to work on save, consider using this package which detects your node modules’ path. )

Anyhow, with these rules:

  • I write const f = (x) => "x" = x= then on save it becomes const f = x => 'x' = x;=
  • I write let xs = [1, 2, 3,] then on save it becomes let xs = [1, 2, 3];
  • I write foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne()); then nothing happens on save; but if you add one more argument, , 12, then it gets reformatted with each arg on one line.

Notice that in the first example above, only minor syntactic changes were made; nothing altering semantics or quality of code —for that, we use a linter. In particular, LSP ESLint when activated will show us two errors: It’s almost always better to use === instead of == and literals should be on the right side of the equality. We can use M-x lsp-execute-code-action to get a list of actions that can be performed to fix this quality problem.

8.9.5 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.10 Semantic and Syntactic Highlighting

8.10.1 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.10.2 Highlight Quoted Symbols

(use-package highlight-quoted
  :config (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode))

;; If everything worked fine, then “ 'b ” below should be coloured nicely in Emacs Lisp mode.
(when nil
  (-let [x 'somevar]
    (list x 'b "c" :e)))

8.10.3 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 separedit-double-quote-string-mode)) ;; The latter is for when I do ⌘-e on a quoted string to edit it.

(use-package highlight-escape-sequences
  :hook ((prog-mode . hes-mode)
         (separedit-double-quote-string-mode . hes-mode)) ;; Wont work since this mode has no font-lock-builtin-face
  :config
  ;; Colour the escapes as if they were builtin keywords.
  (put 'hes-escape-backslash-face 'face-alias 'font-lock-builtin-face)
  (put 'hes-escape-sequence-face 'face-alias 'font-lock-builtin-face))


;; TODO: My Emacs seems to have trouble loading the following and so I'm doint it manually.
(load-file "~/.emacs.d/elpa/company-posframe-20230104.1229/company-posframe.el")
(load-file "~/.emacs.d/elpa/highlight-escape-sequences-20201214.1730/highlight-escape-sequences.el")
(load-file "~/.emacs.d/elpa/parent-mode-20240210.1906/parent-mode.el")
(load-file "~/.emacs.d/elpa/highlight-numbers-20181013.1744/highlight-numbers.el")


;; If the above two worked fine, then you should see \n and 3 highlighted below
(when nil "Look: 1 and \\ and \n 2" (setq three 3))

8.10.4 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)
(load-file "~/.emacs.d/elpa/highlight-defined-20210411.222/highlight-defined.el")

Super helpful in making my Emacs configuration: If a name is not highlighted, then I’ve misspelled it or it doesn’t exist! 😄

8.11 show-me

(defun show-me ()
  "Evaluate a Lisp expression and insert its value
   as a comment at the end of the line.

   Useful for documenting values or checking values.
  "
  (interactive)
  (-let [it
         (thread-last (thing-at-point 'line)
           read-from-string
           car
           eval
           (format " ;; ⇒ %s"))]
    (end-of-line)
    (insert it)))

8.12 Smartparens

(use-package smartparens
  :init
  (smartparens-global-mode 1)
  (show-smartparens-global-mode +1)

  :bind (

         ("M-f" . sp-forward-sexp)  ;; Move forward one  expression.
         ("M-b" . sp-backward-sexp) ;; Move backward one expression.

         ;; Going to the start & end of current expr in pair-able character.
         ("M-a" . sp-beginning-of-sexp)
         ("M-e" . sp-end-of-sexp)

         ;; Going forwards deep down & up current expr; treating it as a tree.
         ("M-d" . sp-down-sexp)
         ("M-u" . sp-up-sexp)
         ;; Acending & descending backwards; i.e., leftwards.
         ("M-n" . sp-backward-down-sexp)
         ("M-p" . sp-backward-up-sexp)

         ;; Unwrapping: Removing pair-able characters.
         ("M-w" . sp-unwrap-sexp)
         ("M-m" . sp-backward-unwrap-sexp)

         ;; “Slurping”: Move closing character forward/backward to include next sexp.
         ;; “Barfing”: Contract a sexp, or string, by pushing a its last/first item out.
         ;; See below for examples.
         ("M-)" . sp-forward-slurp-sexp)
         ("M-(" . sp-backward-slurp-sexp)
         ("M-]" . sp-forward-barf-sexp)
         ("M-[" . sp-backward-barf-sexp)

         ;; Transpose two bracketed terms; e.g., a b c ⟪Here⟫ ⟶ a c b ⟪Here⟫
         ;; Transpose backwards by being on the token;
         ;; transpose forwards by being after the token.
         ("M-t" . sp-transpose-sexp)

         )

  :config
  ;; Enable smartparens everywhere
  (use-package smartparens-config)

  (setq
   ;; smartparens-strict-mode t
   ;; sp-autoinsert-if-followed-by-word t
   ;; sp-autoskip-closing-pair 'always
   sp-hybrid-kill-entire-symbol nil)

  ;; In Elisp & org modes, do not ‘close’ a back-tick or single quote!
  (sp-local-pair 'emacs-lisp-mode "`" nil :when '(sp-in-string-p))
  (sp-local-pair 'emacs-lisp-mode "'" nil :when '(sp-in-string-p))
  (sp-local-pair 'org-mode "`" nil :when '(sp-in-string-p))
  (sp-local-pair 'org-mode "'" nil :when '(sp-in-string-p))
  )

Wrapping To enclose a token with a pair-able character, at the start of the expression press C-ESCAPE-SPACE, select the region, followed by a pair-able character such as [, {, ", ', *, etc. To wrap a single token forwards, use C-M-SPACE.

Examples of slurping & barfing –i.e., sexp inclusion and contraction.

a [x y z] b  ⟶“M-) inside [⋯]”⟶  a [x y z b]
a [x y z] b  ⟶“M-) inside [⋯]”⟶  [a x y z] b
[a x y z b]  ⟶“M-] inside [⋯]”⟶  a [x y z b]
[a x y z b]  ⟶“M-] inside [⋯]”⟶  [a x y z] b

8.13 Column Marker

Have a thin line to the right to ensure I don’t write “off the page”.

(use-package fill-column-indicator
  :hook (prog-mode . fci-mode))
;; (setq fill-column 120)

8.14 Get nice child frames when looking at completions candidates

In particular, when editing Lisp code, the (company-quickhelp) doc strings appear in nice frames when I pause on a completion candidate.
(use-package company-posframe :hook prog-mode)

;; I want to see the doc pop-ups nearly instantaneously 😅
(setq company-posframe-quickhelp-delay 0)

8.15 interactive macro-expander

Anywhere in a macro form, press C-c e to start an (inline) interactive session where you can see what the expanded form of the macro looks like and you can move your cursor to other macro forms and expand those as well by pressing e. Then press q to quit.

(use-package macrostep)
(define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)

This seems like an excellent way to debug macros.

8.16 Smart jumping to definitions

(use-package elisp-def)
(bind-key*  "M-." #'elisp-def emacs-lisp-mode-map)

;; Example usage:
(when nil
  (let ((foo 1))
    (setq foo 2))) ;; “M-.” on this “foo” will now take us to the start of the let-clause.

9 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.
  • See Emacs Writing Studio for a series of articles on making/using Emacs for writing.

9.1 Bidirectional Text

;; Sometimes I have Arabic in my buffers, however I am an English speaker
;; and so Left-to-Right is most natural to me. As such, even when Arabic
;; is present, or any bidirectional text, just use Left-to-Right.
(setq-default bidi-paragraph-direction 'left-to-right)

9.2 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

9.3 Formatting Text

The following incantation, doc:my/org-mode-format, makes it so that we can select some text then press kbd:C-c_f (to get a list of possible character completions) then press the symbol we want our text to be surrounded with.

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

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))
(global-visual-line-mode 1)

Visual line mode is useful when I have way too many windows open or when using smaller frames.

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

9.6 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

  :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 (s-trim (shell-command-to-string "which 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
  :defer 100
  :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
 :defer 100
 :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.

(load "~/dotfiles/.emacs.d/powerthesaurus.el")
(global-set-key (kbd "M-#") 'powerthesaurus-lookup-word-at-point)

;; Website currently down ... https://github.com/SavchenkoValeriy/emacs-powerthesaurus/issues/6

9.7 Touch Typing

Use this game to help you learn to spell words that you’re having trouble with; e.g., I have a file ~~/Dropbox/spelling.txt~ with words I have trouble spelling, which I open then run M-x typing-of-emacs in order to improve spelling said words.

;; The Typing Of Emacs, a game.
(use-package typing-of-emacs
  :quelpa (typing :fetcher wiki :url "https://www.emacswiki.org/emacs/typing.el"))

Practice touch typing using speed-type.

(use-package speed-type )

Running M-x speed-type-region on a region of text, or M-x speed-type-buffer on a whole buffer, or just M-x speed-type-text will produce the selected region, buffer, or random text for practice. The timer begins when the first key is pressed and stats are shown when the last letter is entered.

Other typing resources include:

  • Typing of Emacs —an Emacs alternative to speed type, possibly more engaging.
  • Klavaro —a GUI based yet language-independent typing tutor.
    • I’m enjoying this tool in getting started with Arabic typing.
  • Typing.io is a tutor for coders: Lessons are based on open source code, such some XMonad written in Haskell or Linux written in C.
  • GNU Typist —which is interactive in the terminal, so not ideal in Emacs–,

To assist in language learning, it may be nice to have an Emacs interface to Google translate —e.g., invoke google-translate-at-point.

(use-package google-translate

 :config
   (global-set-key "\C-ct" 'google-translate-at-point))

Select the following then C-c t,

Hey buddy, what’re you up to?

Then detect language then Arabic to obtain:

مرحباً يا صديقي ، ماذا تفعل؟

Neato 😲

9.8 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

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

9.9 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

  :defer 100

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

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

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

9.11 Some text to make us smile

The dad-joke queries https://icanhazdadjoke.com to bring us some funny.

(use-package dad-joke

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

9.12 Unicode Input via Agda Input

;; (load (shell-command-to-string "agda-mode locate"))
;;
;; Seeing: One way to avoid seeing this warning is to make sure that agda2-include-dirs is not bound.
; (makunbound 'agda2-include-dirs)

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

Invoke brew info agda to get these instructions and the version of Agda just installed.

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 "agda-mode locate"))))

I almost always want the agda-mode input method —it’s like the TeX method, but better.

;; TODO: Maybe don't bother installing Agda, and just get agda-input.el
;; from: https://github.com/agda/agda/blob/master/src/data/emacs-mode/agda-input.el
;; then loading that!
(url-copy-file "https://raw.githubusercontent.com/agda/agda/master/src/data/emacs-mode/agda-input.el" "~/.emacs.d/elpa/agda-input.el" :ok-if-already-exists)
(load-file "~/.emacs.d/elpa/agda-input.el")

;; 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))))
“+RTS -H4.5G -M4.5G -K256M -S/tmp/AgdaRTS.log -A1G -RTS -i .”

Wolfram Kahl has recommended the following settings.

;;(setq agda2-program-args (quote ("RTS" "-M4G" "-H4G" "-A128M" "-RTS")))

These arguments specify

+RTS, -RTSFlags between these are arguments to the ghc runtime
-M[size]Maximum heap size
-H[size]Suggested heap size
-A[size]Allocation area size used by the garbage collector

Full documentation for the ghc runtime argumentscan be found here.

Additional arguments that may be useful include

-S[file]Produces information about “each and every garbage collection”
- Outputs to stderr by default

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.

;; install STIX font from Ubuntu store!! ;; (set-fontset-font t ‘unicode (font-spec :name “STIX”) nil ‘append)

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

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

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

9.16 Mouse Editing Support

Text selected with the mouse is automatically copied to clipboard.

(setq mouse-drag-copy-region t)

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

9.18   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 / kbd:M-→ and ahs-backward / kbd: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 doc:defhydra —which gives us temoporary modal modes.)

Finally, let’s use kbd: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.

9.19 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

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

9.20   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 kbd: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

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

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

9.22 LaTeX ⇐ Org-Mode

In this section we consider the Org-mode export for PDFs (LaTeX). For example, we account for LaTeX citations.

#

9.22.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 kbd: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

;; Use 3 headlines of export, which is the default
;; (setq org-export-headline-levels 4)

;; no numbers by default at export
;; (setq org-export-with-section-numbers nil)

9.22.2 Working with Citations

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:agda_overview ( 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.

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

9.23 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 )
;; Main use: Org produced htmls are coloured.
;; Can be used to export a file into a coloured html.

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

Hey bud, hope you’re enjoying this read ^_^

Now that I’ve written this, I’m thinking it may have been preferably to use an org-block…?

9.23.2 Diagrams with Mermaid —Not Recommended

Let’s try out an alternative to PlantUML —covered below in §Workflow States.

First, let’s get the tool.

npm install mermaid.cli
sudo git clone [email protected]: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:

sequenceDiagram
A-->B: Works!
Loading
  • 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.

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

Here (source) is an example of org-reveal slides where I add a number to each page, use multiple columns, and extend the margins perhaps a bit too much.

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.

9.24 C-c C-l Org-mode ⇐ HTML

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

10 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 ♥‿♥ ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ

10.1 Done!

(add-to-list 'default-frame-alist '(fullscreen . maximized))
(message-box "Enjoy life (。◕‿◕。))")