I enjoy reading others’ literate configuration files and incorporating what I learn into my own. The result is a sufficiently well-documented and accessible read that yields a stylish and functional system (•̀ᴗ•́)و
This README.org
has been automatically generated from my
configuration and its contents below are accessible
in (outdated) blog format, with colour, or as colourful
PDF, here. Enjoy
Abstract
Herein I document the configurations I utilise with Emacs.
As a literate program file with Org-mode, I am ensured optimal navigation through my ever growing configuration files, ease of usability and reference for peers, and, most importantly, better maintainability for myself!
Dear reader, when encountering a foregin command X
I encourage you to execute
(describe-symbol 'X)
, or press C-h o
with the cursor on X
. An elementary Elisp
Cheat Sheet can be found here and here is a 2-page 3-column Emacs Cheat Sheet of
the bindings in “this”
configuration.
C-h o
⇒ What’s this thing?C-h e
⇒ What’d /Emacs/ do?C-h l
⇒ What’d /I/ do?- “I accidentally hit a key, which one and what did it do!?” ⇒
C-h e
andC-h l
, then useC-h o
to get more details on the action. ;-)
Finally, C-h d
asks nicely what ‘d’ocumentation you’re interested in.
After providing a few keywords, the apropos
tool yields possible functions
and variables that may accomplish my goal.
- Why Emacs?
- Booting Up
- =~/.emacs= vs. =init.org=
- =use-package= —The start of =init.el=
- =README= —From
init.org
to =init.el= - Installing Emacs packages directly from source
- =magit= —Emacs’ porcelain interface to gitq
- Syncing to the System’s =$PATH=
- Installing OS packages, and automatically keeping my system up to data, from within Emacs
- “Being at the Helm” —Completion & Narrowing Framework
- Having a workspace manager in Emacs
- Excellent PDF Viewer
- Who am I? —Using Gnus for Gmail
- Hydra: Supply a prefix only once
- Quickly pop-up a terminal, run a command, close it —and zsh
- Restarting Emacs —Keeping buffers open across sessions?
- Automatic Backups
- Editor Documentation with Contextual Information
- Cosmetics
- Startup message: Emacs & Org versions
- My to-do list: The initial buffer when Emacs opens up
- Exquisite Themes
- A sleek & informative mode line
- Never lose the cursor
- Dimming Unused Windows
- Buffer names are necessarily injective
- Flashing when something goes wrong —no blinking
- Hiding Scrollbar, tool bar, and menu
- Highlight & complete parenthesis pair when cursor is near ;-)
- Neotree: Directory Tree Listing
- Tabs
- Window resizing using the golden ratio
- Persistent Scratch Buffer
- Prose
- Fill-mode —Word Wrapping
- Word Completion
- Fix spelling as you type —thesaurus & dictionary too!
- Touch Typing
- Using a Grammar & Style Checker
- Lightweight Prose Proofchecking
- Placeholder Text —For Learning & Experimenting
- Some text to make us smile
- Unicode Input via Agda Input
- Increase/decrease text size
- Moving Text Around
- Enabling CamelCase Aware Editing Operations
- Mouse Editing Support
- Delete Selection Mode
- ~M-n,p~: Word-at-Point Navigation
- Letter-based Navigation
- ~C-c e n,p~: Taking a tour of one’s edits
- Org-Mode Administrivia
- Executing code from
src
blocks - High Speed Literate Programming
- Hiding Emphasise Markers & Inlining Images
- Org-Emphasise for Parts of Words
- Show off-screen heading at the top of the window
- Jumping without hassle
- Folding within a subtree
- Making Block Delimiters Less Intrusive
- Org-mode’s
<𝒳
Block Expansions
- Executing code from
- Org-Mode ⇒ PDF & HTML
- Life within Org-mode
- Programming
- Highlight defined Lisp symbols
- Eldoc for Lisp and Haskell
- Jumping to definitions & references
- Aggressive Indentation
- What’s changed & who’s to blame?
- Highlighting TODO-s & Showing them in Magit
- On the fly syntax checking
- Coding with a Fruit Salad: Semantic Highlighting
- Text Folding with Origami-mode
- Jump between windows using Cmd+Arrow & between recent buffers with Meta-Tab
- Snippets —Template Expansion
- Helpful Utilities & Shortcuts
- Documentation Pop-Ups
- Emacs keybindings for my browser
- Using Emacs in any text area on my OS
- Reload buffer with ~f5~
- Kill to start of line
- Killing buffers & windows:
C-x k
has a family - Switching from 2 horizontal windows to 2 vertical windows
- Obtaining Values of
#+KEYWORD
Annotations - Publishing articles to my personal blog
- Conclusion —Why Configuration Files Should be Literate
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
Of course Emacs comes with the basic features of a text editor, but it is much more; for example, it comes with a powerful notion of ‘undo’: Basic text editors have a single stream of undo, yet in Emacs, we have a tree –when we undo and make new edits, we branch off in our editing stream as if our text was being version controlled as we type! –We can even switch between such branches!
;; Allow tree-semantics for undo operations.
(use-package undo-tree
:diminish ;; Don't show an icon in the modeline
:config
;; Always have it on
(global-undo-tree-mode)
;; Each node in the undo tree should have a timestamp.
(setq undo-tree-visualizer-timestamps t)
;; Show a diff window displaying changes between undo nodes.
(setq undo-tree-visualizer-diff t))
;; Execute (undo-tree-visualize) then navigate along the tree to witness
;; changes being made to your file live!
( The above snippet has a noweb-ref
: It is presented here in a natural
position, but is only executable once use-package
is setup and so it
is weaved there! We can present code in any order and tangle it to
the order the compilers need it to be! )
Emacs is an extensible editor: You can make it into the editor of your dreams!
You can make it suited to your personal needs.
If there’s a feature you would like, a behaviour your desire, you can simply code that into Emacs with
a bit of Lisp. As a programming language enthusiast, for me Emacs is my default Lisp interpreter
and a customisable IDE that I use for other programming languages
–such as C, Haskell, Agda, Lisp, and Prolog.
Moreover, being a Lisp interpreter, we can alter the look and feel of Emacs live, without having
to restart it –e.g., press C-x C-e
after the final parenthesis of (scroll-bar-mode 0)
to run the code that removes the scroll-bar.
I use Emacs every day. I rarely notice it. But when I do, it usually brings me joy. ─Norman Walsh
I have used Emacs as an interface for developing cheat sheets, for making my blog, and as an application for ‘interactively learning C’. If anything Emacs is more like an OS than just a text editor –“living within Emacs” provides an abstraction over whatever operating system my machine has: It’s so easy to take everything with me. Moreover, the desire to mould Emacs to my needs has made me a better programmer: I am now a more literate programmer and, due to Elisp’s documentation-oriented nature, I actually take the time and effort to make meaningful documentation –even when the project is private and will likely only be seen by me.
Seeing Emacs as an editor is like seeing a car as a seating-accommodation. – Karl Voit
Possibly interesting reads:
- How to Learn Emacs: A Hand-drawn One-pager for Beginners / A visual tutorial
- Video Series on Why Emacs Rocks —catch the enthusiasm!
- Emacs org-mode examples and cookbook
- An Opinionated Emacs guide for newbies and beyond
- Emacs Mini-Manual, Part I of III
- Org and R Programming —a tutorial on literate programming, e.g., evaluating code within
src
bloc. - Reference cards for GNU Emacs, Org-mode, and Elisp.
- “When did you start using Emacs” discussion on Reddit
- “How to Learn Emacs”
- The Org-mode Reference Manual or Worg: Community-Written Docs which includes a meta-tutorial.
- Awesome Emacs: A community driven list of useful Emacs packages, libraries and others.
- A list of people’s nice emacs config files
- Read Lisp, Tweak Emacs: How to read Emacs Lisp so that you can customize Emacs
- Why Racket? Why Lisp?
—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
The Power of Text Manipulation: Emacs has ways to represent all kinds of information as text.
E.g., if want to make a regular expression rename of files in a directory,
there’s no need to learn about a batch renaming tool: M-x dired <RET> M-x wdired-change-to-wdired-mode
now simply perform a usual find-and-replace, then
save with the usual C-x C-s
to effect the changes!
Likewise for other system utilities and services (•̀ᴗ•́)و
Moreover, as will be shown below, you can literally use Emacs anywhere for textually input in your operating system –no copy-paste required.
Keyboard Navigation and Alteration Suppose you wrote a paragraph of text, and
wanted to ‘border’ it up for emphasies in hypens. Using the mouse to navigate
along with a copy-paste of the hypens is vastely inferior to the incantation M-{
C-u 80 - RET M-} C-u 80 - RET
. If we want to border up the previous 𝓃-many
paragraphs, we simply prefix M-{,}
above with C-u 𝓃
—a manual approach would
have us count 𝓃 and slowly scroll. ( Exercise: What incantation of keys
‘underlines’ the current line with only the necessary amount of dashes?
—Solution in the source file. )
Finally, here’s some fun commands to try out:
M-x doctor
—generalising the idea of rubber ducksM-x tetris
orM-x gomoku
orM-x snake
—a break with a classicC-u 𝓃 M-x hanoi
for the 𝓃-towers of Hanoi
M-x butterfly
—in reference to “real programmers”
Before we get started…
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.
⟨ This image was created in org-mode; details below. ⟩
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!
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.
Emacs is extenible: When Emacs is started, it tried to load a user’s Lisp
program known as a initialisation file which specfies how Emacs should look and
behave for you. Emacs looks for the init file using the filenames ~/.emacs.el,
~/.emacs,
or ~/.emacs.d/init.el
—it looks for the first one that exists, in
that order; at least it does so on my machine. Below we’ll avoid any confusion
by ensuring that only one of them is in our system. Regardless, execute C-h o
user-init-file
to see the name of the init file loaded. Having no init file is
tantamount to have an empty init file.
- One can read about the various Emacs initialisation files online or
within Emacs by the sequence
C-h i m emacs RET i init file RET
. - A friendly tutorial on ‘beginning a
.emacs
file’ can be read online or within Emacs byC-h i m emacs lisp intro RET i .emacs RET
. - After inserting some lisp code and saving, such as
(set-background-color "salmon")
, one can load the changes withM-x eval-buffer
. - In a terminal, use
emacs -Q
to open emacs without any initialisation files.
Besides writing Lisp in an init file, one may use Emacs’ customisation
interface, M-x customize
: Point and click to change Emacs to your needs. The
resulting customisations are, by default, automatically thrown into your init
file —=~/.emacs= is created for you if you have no init file. This interface is
great for beginners, but one major drawback is that it’s a bit difficult to
share settings since it’s not amicable to copy-pasting.
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 fromat, 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.
Let’s use the three possible locations for the initialisation files to explore how Emacs finds them. Make the following three files.
~/.emacs.el
;; Emacs looks for this first;
(set-background-color "chocolate3")
(message-box ".emacs.el says hello")
~/.emacs
;; else; looks for this one;
(set-background-color "plum4")
(message-box ".emacs says hello")
~/.emacs.d/init.el
;; Finally, if neither are found; it looks for this one.
(set-background-color "salmon")
(message-box ".emacs.d/init.el says hello")
Now restart your Emacs to see how there super tiny initilaisation files affect your editor. Delete some of these files in-order for others to take effect!
We have chosen not to keep configurations in ~~/.emacs~ since Emacs may explicitly add, or alter, code in it.
Let’s see this in action!
Execute the following to see additions to the ~~/.emacs~ have been added by ‘custom’.
M-x customize-variable RET line-number-mode RET
- Then press:
toggle
,state
, then1
. - Now take a look:
C-x C-f ~/.emacs
Let the Emacs customisation GUI insert configurations into its own file, not
touching or altering my initialisation file. For example, I tend to have local
variables to produce README.org
’s and other matters, so Emacs’ Custom utility
will remember to not prompt me each time for the safety of such local variables.
(setq custom-file "~/.emacs.d/custom.el")
(load custom-file)
Speaking of local variables, let’s always 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)
There are a few ways to install packages —run C-h C-e
for a short overview.
The easiest, for a beginner, is to use the command package-list-packages
then
find the desired package, press i
to mark it for installation, then install all
marked packages by pressing x
.
- Interactively:
M-x list-packages
to see all melpa packages that can install- Press
Enter
on a package to see its description.
- Press
- Or more quickly, to install, say, the haskell mode:
M-x package-install RET unicode-fonts RET
.
“From rags to riches”: Recently I switched to Mac —first time trying the OS.
I had to do a few package-install
’s and it was annoying. I’m looking for the
best way to package my Emacs installation —including my installed packages and
configuration— so that I can quickly install it anywhere, say if I go to
another machine. It seems use-package
allows me to configure and auto install
packages. On a new machine, when I clone my .emacs.d
and start Emacs, on the
first start it should automatically install and compile all of my packages
through use-package
when it detects they’re missing.
First we load package
, the built-in package manager. It is by default only
connected to the GNU ELPA (Emacs Lisp Package Archive) repository, so we
extended it with other popular repositories; such as the much larger MELPA
(Milkypostman’s ELPA) —it builds packages directly from the source-code
reposistories 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 '(("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")
("melpa" . "http://melpa.org/packages/")
("melpa-stable" . "http://stable.melpa.org/packages/")))
;; Actually get “package” to work.
(package-initialize)
(package-refresh-contents)
- All installed packages are placed, by default, in
~/.emacs.d/elpa
. - Neato: If one module requires others to run, they will be installed automatically.
The declarative configuration tool use-package is a macro/interface that manages other packages and the way they interact.
- It allows us to tersely organise a package’s configuration.
- By default,
(use-package foo)
only loads a package, if it’s on our system.- Use the standalone keyword
:disabled
to turn off loading a module that, say, you’re not using anymore.
- Use the standalone keyword
- By default,
- It is not a package manger, but we can make it one by having it automatically
install modules, via Emacs packing mechanism, when they’re not in our system.
We achieve this by using the keyword option
:ensure t
. - Here are common keywords we will use, in super simplified terms.
:init f₁ … fₙ
Always executes code formsfᵢ
before loading a package.:diminish str
Uses optional stringstr
in the modeline to indicate this module is active. Things we use often needn’t take real-estate down there and so no we provide nostr
.:config f₁ … fₙ
Only executes code formsfᵢ
after loading a package.The remaining keywords only take affect after a module loads.
:bind ((k₁ . f₁) … (kₙ . fₙ)
Lets us bind keyskᵢ
, such as"M-s o"
, to functions, such asoccur
.- When n = 1, the extra outer parenthesis are not necessary.
:hook ((m₁ … mₙ) . f)
Enables functionalityf
whenever we’re in one of the modesmᵢ
, such asorg-mode
. The. f
, along with the outermost parenthesis, is optional and defaults to the name of the package —Warning: Erroneous behaviour happens if the package’s name is not a function provided by the package; a common case is when package’s name does not end in-mode
, leading to the invocation((m₁ … mₙ) . <whatever-the-name-is>-mode)
instead.Additionally, when n = 1, the extra outer parenthesis are not necessary.
Outside of
use-package
, one normally uses aadd-hook
clause. Likewise, an ‘advice’ can be given to a function to make it behave differently —this is known as ‘decoration’ or an ‘attribute’ in other languages.:custom (k₁ v₁ d₁) … (kₙ vₙ dₙ)
Sets a package’s custom variableskᵢ
to have valuesvᵢ
, along with optional user documentationdᵢ
to explain to yourself, in the future, why you’ve made this decision.This is essentially
setq
within:config
.
We now bootstrap use-package
.
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(eval-when-compile (require 'use-package))
We can now invoke (use-package XYZ :ensure t)
which should check for the XYZ
package and make sure it is accessible. If not, 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)
The use of :ensure t
only installs absent modules, but it does no updating.
Let’s set up an auto-update mechanism.
(use-package auto-package-update
:config
;; Delete residual old versions
(setq auto-package-update-delete-old-versions t)
;; Do not bother me when updates have taken place.
(setq auto-package-update-hide-results t)
;; Update installed packages at startup if there is an update pending.
(auto-package-update-maybe))
Here’s another example use of use-package
. Later on, I have a “show recent files
pop-up” command set to C-x C-r
; but what if I forget? This mode shows me all key
completions when I type C-x
, for example. Moreover, I will be shown other
commands I did not know about! Neato :-)
;; Making it easier to discover Emacs key presses.
(use-package which-key
:diminish
:config (which-key-mode)
(which-key-setup-side-window-bottom)
(setq which-key-idle-delay 0.05))
⟨ Honestly, I seldom even acknowledge this pop-up; but it’s always nice to show to people when I’m promoting Emacs. ⟩
Above, the :diminish
keyword indicates that we do not want the mode’s name to be
shown to us in the modeline —the area near the bottom of Emacs. It does so by
using the diminish
package, so let’s install that.
(use-package diminish
:demand t
:config ;; Let's hide some markers.
(diminish 'org-indent-mode))
Here are other packages that I want to be installed onto my machine.
;; Efficient version control.
;;
;; Bottom of Emacs will show what branch you're on
;; and whether the local file is modified or not.
(use-package magit
:config (global-set-key (kbd "C-x g") 'magit-status))
(use-package htmlize)
;; Main use: Org produced htmls are coloured.
;; Can be used to export a file into a coloured html.
;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★
;; https://github.com/emacsorphanage/org-bullets
(use-package org-bullets
:hook (org-mode . org-bullets-mode))
;; Haskell's cool
(use-package haskell-mode)
;; Lisp libraries with Haskell-like naming.
(use-package dash) ;; “A modern list library for Emacs”
(use-package s ) ;; “The long lost Emacs string manipulation library”.
;; Library for working with system files;
;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden?
(use-package f)
Note:
- dash: “A modern list library for Emacs”
- E.g.,
(--filter (> it 10) (list 8 9 10 11 12))
- E.g.,
- s: “The long lost Emacs string manipulation library”.
- E.g.,
s-trim, s-replace, s-join
.
- E.g.,
Remember that snippet for undo-tree
in the introductory section?
Let’s activate it now, after use-package
has been setup.
<<undo-tree-setup>>
Rather than manually extracting the Lisp code from this literate document each
time we alter it, let’s instead add a ‘hook’ —a method that is invoked on a
particular event, in this case when we save the file. More precisely, in this
case, C-x C-s
is a normal save whereas C-u C-x C-s
is a save after forming
init.elc
and README.md
.
We ‘hook on’ the following function to the usual save method that is associated with this file only.
(defun my/make-init-el-and-README ()
"Tangle an el and a github README from my init.org."
(interactive "P") ;; Places value of universal argument into: current-prefix-arg
(when current-prefix-arg
(let* ((time (current-time))
(_date (format-time-string "_%Y-%m-%d"))
(.emacs "~/.emacs")
(.emacs.el "~/.emacs.el"))
;; Make README.org
(save-excursion
(org-babel-goto-named-src-block "make-readme") ;; See next subsubsection.
(org-babel-execute-src-block))
;; remove any other initialisation file candidates
(ignore-errors
(f-move .emacs (concat .emacs _date))
(f-move .emacs.el (concat .emacs.el _date)))
;; Make init.el
(org-babel-tangle)
(byte-compile-file "~/.emacs.d/init.el")
(load-file "~/.emacs.d/init.el")
;; Acknowledgement
(message "Tangled, compiled, and loaded init.el; and made README.md … %.06f seconds"
(float-time (time-since time))))))
(add-hook 'after-save-hook 'my/make-init-el-and-README nil 'local-to-this-file-please)
Where the following block has #+NAME: make-readme
before it. This source block
generates the README
for the associated Github repository.
(save-buffer)
(with-temp-buffer
(insert
"#+EXPORT_FILE_NAME: README.org
# Logos and birthday present painting
#+HTML:" (s-collapse-whitespace (concat
" <p align=\"center\">
<img src=\"images/emacs-logo.png\" width=150 height=150/>
</p>
<p align=\"center\">
<a href=\"https://www.gnu.org/software/emacs/\">
<img src=\"https://img.shields.io/badge/GNU%20Emacs-" emacs-version "-b48ead.svg?style=plastic\"/></a>
<a href=\"https://orgmode.org/\"><img src=\"https://img.shields.io/badge/org--mode-" org-version "-489a9f.svg?style=plastic\"/></a>
</p>
<p align=\"center\">
<img src=\"images/emacs-birthday-present.png\" width=300 height=250/>
</p>
"))
;; My Literate Setup; need the empty new lines for the export
"
I enjoy reading others' /literate/ configuration files and
incorporating what I learn into my own. The result is a
sufficiently well-documented and accessible read that yields
a stylish and functional system (•̀ᴗ•́)و
This ~README.org~ has been automatically generated from my
configuration and its contents below are accessible
in (outdated) blog format, with /colour/, or as colourful
PDF, [[https://alhassy.github.io/init/][here]]. Enjoy
:smile:
#+INCLUDE: init.org
")
;; No code execution on export
;; ⟪ For a particular block, we use “:eval never-export”. ⟫
(let ((org-export-use-babel nil))
(org-mode)
(org-org-export-to-org)))
Alternatively, evaluate the above source block with C-c C-c
to produce a README
file.
For the ‘badges’, see https://shields.io/. The syntax above is structured:
https://img.shields.io/badge/<LABEL>-<MESSAGE>-<COLOR>.svg
The above mentioned package toc-org, which creates an up-to-date table of
contents in an org file, at any heading tagged :TOC:
. It’s useful primarily for
README files on Github. There is also org-make-toc, which is more flexible: The
former provides only a top-level TOC; whereas this package allows TOCs at the
sibling level, say, to produce a TOC of only the subsections of a particular
heading, and other TOC features. Unlike toc-org, org-make-toc uses property drawers
to designate TOC matter.
(use-package toc-org
;; Use both “:ignore_N:” and ":export_N:” to exlude headings from the TOC.
:custom (toc-org-noexport-regexp
"\\(^*+\\)\s+.*:\\(ignore\\|noexport\\)\\([@_][0-9]\\)?:\\($\\|[^ ]*?:$\\)")
;; Automatically update toc when saving an Org file.
:hook (org-mode . toc-org-mode))
However, toc-org produces broken links for numbered sections.
That is, if we use #+OPTIONS: num:t
then a section, say
** =~/.emacs= vs. =init.org=
as the first subheading of the third
heading, then it renders with the text preceeded by 3.1
.
On the left-most part of the heading, Github provides a a link option;
clicking provides a link to this exact location in the README,
changing the current URL to something like
https://github.com/alhassy/emacs.d#31-emacs-vs-initorg
.
Now, toc-org produces Github-style anchors from Org headings,
but does not account for numbers, and so gives us
https://github.com/alhassy/emacs.d#emacs-vs-initorg
, which is
so close but missing the translated number, 31
.
I’ve experimented with using toc-org links using org-style, instead of the default Github style, but it seems that the org-style completely breaks rendering the resulting readme. Likewise, it seems that headings that are links break the TOC link; whence my section on the Reveal slide-deck system has a broken link to it. Perhaps org-make-toc solves these issues —something to look into.
I’m not sure how I feel about actually having the Github-serving TOC in my
source file. It’s nice to have around, from an essay-perspecive, but it breaks
HTML export since its links are not well-behaved; e.g., :ignore:
-ed headlines
appear in the toc, but do not link to any visible heading in the HTML; likewise,
headings with URLS in their names break. As such, below I’ve developed a way to
erase it altogether —alternatively, one could mark the toc as :noexport:
, but
this would then, in my current approach, not result in a toc in the resulting
README.
(cl-defun my/org-replace-tree-contents (heading &key (with "") (offset 0))
"Replace the contents of org tree HEADING with WITH, starting at OFFSET.
Clear a subtree leaving first 3 lines untouched ⇐ :offset 3
Deleting a tree & its contents ⇐ :offset -1, or any negative number.
Do nothing to a tree of 123456789 lines ⇐ :offset 123456789
Precondition: offset < most-positive-fixnum; else we wrap to a negative number."
(interactive)
(save-excursion
(beginning-of-buffer)
(re-search-forward (format "^\\*+ %s" (regexp-quote heading)))
;; To avoid ‘forward-line’ from spilling onto other trees.
(org-narrow-to-subtree)
(org-mark-subtree)
;; The 1+ is to avoid the heading.
(dotimes (_ (1+ offset)) (forward-line))
(delete-region (region-beginning) (region-end))
(insert with)
(widen)))
;; Erase :TOC: body ---provided we're using toc-org.
;; (my/org-replace-tree-contents "Table of Contents")
Github supports several markup languages, one of which is Org-mode.
- It seems that Github uses org-ruby to convert org-mode to html.
- Here is a repo demonstrating how Github interprets Org-mode files.
- org-ruby supports inline
#+HTML
but not html blocks.
It seems coloured HTML does not render well:
(org-html-export-to-html) (shell-command "mv README.html README.md")
JavaScript supported display of web pages with:
#+INFOJS_OPT: view:info toc:t buttons:t
This looks nice for standalone pages, but doesn’t incorporate nicely with github README.org.
Usually, Github readme files are in markdown, which we may obtain from an Org
file with M-x org-md-export-to-markdown
.
- [ ] By default, this approach results in grey-coloured source blocks —eek!
- [X] It allows strategic placement of a table of contents.
Declare
#+options: toc:nil
at the top of the Org file, then have#+TOC: headlines 2
in a strategic position for a table of contents, say after a brief explanation of what the readme is for. - [X] It allows us to preview the readme locally before comitting, using grip.
;; grip looks for README.md
(system-packages-ensure "grip")
;; Next: (async-shell-command "cd ~/.emacs.d/; grip")
We can approximate this behaviour for the other approaches:
- Export to markdown.
COMMENT
-out any:TOC:
-tagged sections —their links are not valid markdown links, since they don’t refer to any markdown labels.- Rename the exported file to
README.md
. - Run
grip
.
Quelpa allows us to build Emacs packages directly from source repositories. It
derives its name from the German word Quelle, for souce [code], adjoined to
ELPA. Its use-package
interface allows us to use use-package
like normal but
when we want to install a file from souce we use the keyword :quelpa
.
(use-package quelpa
:custom (quelpa-upgrade-p t "Always try to update packages")
:config
;; Get ‘quelpa-use-package’ via ‘quelpa’
(quelpa
'(quelpa-use-package
:fetcher git
:url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package))
Let’s use this to obtain an improved info-mode from the EmacsWiki. [Disabled for now]
(use-package info+
:disabled
:quelpa (info+ :fetcher wiki :url "https://www.emacswiki.org/emacs/info%2b.el"))
Let’s setup an Emacs ‘porcelain’ interface to git —it makes working with version control tremendously convenient. Moreover, I add a little pop-up so that I don’t forget to commit often!
Why use magit
as the interface to the git version control system? In magit
buffer nearly everything can be acted upon: Press return
, or space
, to see
details and tab
to see children items, usually.
First, let’s setup our git credentials.
;; See here for a short & useful tutorial:
;; https://alvinalexander.com/git/git-show-change-username-email-address
(when (equal ""
(shell-command-to-string "git config user.name"))
(shell-command "git config --global user.name \"Musa Al-hassy\"")
(shell-command "git config --global user.email \"[email protected]\""))
Below is my personal quick guide to working with magit —for a full tutorial see jr0cket’s blog.
dired
- See the contents of a particular directory.
magit-init
- Put a project under version control.
The mini-buffer will prompt you for the top level folder version.
A
.git
folder will be created there. magit-status
,C-x g
- See status in another buffer.
Press
?
to see options, including:- g
- Refresh the status buffer.
- TAB
- See collapsed items, such as what text has been changed.
q
- Quit magit, or go to previous magit screen.
s
- Stage, i.e., add, a file to version control.
Add all untracked files by selecting the Untracked files title.
The staging area is akin to a pet store; commiting is taking the pet home.
k
- Kill, i.e., delete a file locally.
K
- This’
(magit-file-untrack)
which doesgit rm --cached
. i
- Add a file to the project
.gitignore
file. Nice stuff =) u
- Unstage a specfif staged change highlighed by cursor.
C-u s
stages everything –tracked or not. c
- Commit a change.
- A new buffer for the commit message appears, you write it then
commit with
C-c C-c
or otherwise cancel withC-c C-k
. These commands are mentioned to you in the minibuffer when you go to commit. - You can provide a commit to each altered chunk of text!
This is super neat, you make a series of local such commits rather
than one nebulous global commit for the file. The
magit
interface makes this far more accessible than a standard terminal approach! - You can look at the unstaged changes, select a region, using
C-SPC
as usual, and commit only that if you want! - When looking over a commit,
M-p/n
to efficiently go to previous or next altered sections. - Amend a commit by pressing
a
onHEAD
.
- A new buffer for the commit message appears, you write it then
commit with
d
- Show differences, another
d
or another option.- This is magit! Each hunk can be acted upon; e.g.,
s
orc
ork
;-)
- This is magit! Each hunk can be acted upon; e.g.,
v
- Revert a commit.
x
- Undo last commit. Tantamount to
git reset HEAD~
when cursor is on most recent commit; otherwise resets to whatever commit is under the cursor. l
- Show the log, another
l
for current branch; other options will be displayed.- Here
space
shows details in another buffer while cursour remains in current buffer and, moreover, continuing to pressspace
scrolls through the other buffer! Neato.
- Here
P
- Push.
F
- Pull.
:
- Execute a raw git command; e.g., enter
whatchanged
.
Notice that every time you press one of these commands, a ‘pop-up’ of realted git options appears! Thus not only is there no need to memorise many of them, but this approach makes discovering other commands easier.
Below are the git repos I’d like to clone —along with a function to do so quickly.
(use-package magit)
;; Do not ask about this variable when cloning.
(setq magit-clone-set-remote.pushDefault t)
(cl-defun maybe-clone (remote &optional (local (concat "~/" (file-name-base remote))))
"Clone a REMOTE repository if the LOCAL directory does not exist.
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."
(if (file-directory-p local)
'repo-already-exists
(async-shell-command (concat "git clone " remote " " local))
(add-to-list 'magit-repository-directories `(,local . 0))
'cloned-repo))
(maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d")
(maybe-clone "https://github.com/alhassy/alhassy.github.io")
(maybe-clone "https://github.com/alhassy/CheatSheet")
(maybe-clone "https://github.com/alhassy/ElispCheatSheet")
(maybe-clone "https://github.com/alhassy/CatsCheatSheet")
(maybe-clone "https://github.com/alhassy/islam")
;; For brevity, many more ‘maybe-clone’ clauses are hidden in the source file.
Let’s always notify ourselves of a file that has uncommited changes —we might have had to step away from the computer and forgotten to commit.
(require 'magit-git)
(defun my/magit-check-file-and-popup ()
"If the file is version controlled with git
and has uncommitted changes, open the magit status popup."
(let ((file (buffer-file-name)))
(when (and file (magit-anything-modified-p t file))
(message "This file has uncommited changes!")
(when nil ;; Became annyoying after some time.
(split-window-below)
(other-window 1)
(magit-status)))))
;; I usually have local variables, so I want the message to show
;; after the locals have been loaded.
(add-hook 'find-file-hook
'(lambda ()
(add-hook 'hack-local-variables-hook 'my/magit-check-file-and-popup)))
Finally, one of the main points for using version control is to have access to
historic versions of a file. The following utility allows us to M-x
git-timemachine
on a file and use p/n/g/q
to look at previous, next, goto
arbitrary historic versions, or quit.
(use-package git-timemachine)
If we want to roll back to a previous version, we just write-file
or C-x C-s
as
usual! The power of text!
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.
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.
Let’s obtain the extension.
;; Auto installing OS system packages
(use-package use-package-ensure-system-package)
;; 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 ^_^
(system-packages-update)
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 use-package-ensure-system-package documentation for a flurry of use cases.
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.
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)
;; 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"))
;; If the given system package doesn't exist; install it.
(system-packages-ensure "amethyst")
Neato! Now I can live in Emacs even more ^_^
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! Press M-x
and just start typing, methods mentioning
what you’ve typed are suddenly listed!
Remembrance comes with time, until then ask Emacs! |
Try and be grateful!
(use-package helm
:diminish
:init (helm-mode t)
:bind (("M-x" . helm-M-x)
("C-x C-f" . helm-find-files)
("C-x b" . helm-mini) ;; See buffers & recent files; more useful.
("C-x r b" . helm-filtered-bookmarks)
("C-x C-r" . helm-recentf) ;; Search for recently edited files
("C-c i" . helm-imenu)
("C-h a" . helm-apropos)
;; Look at what was cut recently & paste it in.
("M-y" . helm-show-kill-ring)
: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)))
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 withhelm-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))
: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!
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 ;-)
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 😃
Let’s install the pdf-tools library for viewing PDFs in Emacs.
(use-package pdf-tools
; :init (system-packages-ensure "pdf-tools")
:custom (pdf-tools-handle-upgrades nil)
(pdf-info-epdfinfo-program "/usr/local/bin/epdfinfo")
:config (pdf-tools-install))
;; Now PDFs opened in Emacs are in pdfview-mode.
Besides the expected PDF viewing utilities, such as search, annotation, and continuous scrolling; with a simple mouse right-click, we can even select a ‘midnight’ rendering mode which may be easier on the eyes. For more, see the brief pdf-tools-tourdeforce demo.
Let’s set the following personal Emacs-wide variables —to be used in other locations besides 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)
By default, in Emacs, we may send mail: Write it in Emacs with C-x m
,
then press C-c C-c
to have it sent via your OS’s default mailing system
—mine appears to be Gmail via the browser. Or cancel sending mail with
C-c C-k
—the same commands for org-capturing, discussed below (•̀ᴗ•́)و
To send and read email in Emacs we use GNUS, which, like GNU itself, is a recursive acronym: GNUS Network User Service.
- Execute, rather place in your init:
(setq message-send-mail-function 'smtpmail-send-it)
Revert to the default OS mailing method by setting this variable to
mailclient-send-it
. - Follow only the quickstart here; namely, make a file named ~~/.gnus~ containing:
;; user-full-name and user-mail-address should be defined (setq gnus-select-method '(nnimap "gmail" (nnimap-address "imap.gmail.com") (nnimap-server-port "imaps") (nnimap-stream ssl))) (setq smtpmail-smtp-server "smtp.gmail.com" smtpmail-smtp-service 587 gnus-ignored-newsgroups "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]\"[#'()]")
- Enable “2 step authentication” for Gmail following these instructions.
- You will then obtain a secret password, the
x
marks below, which you insert in a file named ~~/.authinfo~ as follows —using your email address.machine imap.gmail.com login [email protected] password xxxxxxxxxxxxxxxx port imaps machine smtp.gmail.com login [email protected] password xxxxxxxxxxxxxxxx port 587
- In Emacs,
M-x gnus
to see what’s there.Or compose mail with
C-x m
then send it withC-c C-c
.- Press
C-h m
to learn more about message mode for mail composition; or read the Message Manual.
- Press
;; After startup, if Emacs is idle for 5seconds, then start Gnus.
;; Gnus is slow upon startup since it fetches all mails upon startup.
;; (run-with-idle-timer 5 nil #'gnus)
Learn more by reading The Gnus Newsreader Manual; also available within Emacs by
C-h i m gnus
(•̀ᴗ•́)و
- Or look at the Gnus Reference Card.
- Or, less comprehensively, this outline.
EmacsWiki has a less technical and more user friendly tutorial.
Let’s add the icon near my mail groups ^_^
;; Fancy icons for Emacs
;; Only do this once:
(use-package all-the-icons)
; :config (all-the-icons-install-fonts 'install-without-asking)
;; Make mail look pretty
(use-package all-the-icons-gnus
:config (all-the-icons-gnus-setup))
;; While we're at it: Make dired, ‘dir’ectory ‘ed’itor, look pretty
(use-package all-the-icons-dired
:hook (dired-mode . all-the-icons-dired-mode))
Next, let’s paste in some eye-candy for Gnus:
(setq gnus-sum-thread-tree-vertical "│"
gnus-sum-thread-tree-leaf-with-other "├─► "
gnus-sum-thread-tree-single-leaf "╰─► "
gnus-summary-line-format
(concat
"%0{%U%R%z%}"
"%3{│%}" "%1{%d%}" "%3{│%}"
" "
"%4{%-20,20f%}"
" "
"%3{│%}"
" "
"%1{%B%}"
"%s\n"))
In gnus, by default items you’ve looked at disappear —i.e., are archived.
They can still be viewed in, say, your online browser if you like.
In the Group
view, R
resets gnus, possibly retriving mail or alterations
from other mail clients. q
exits gnus in Group
mode, q
exits the particular
view to go back to summary mode. Only after pressing q
from within a group
do changes take effect on articles —such as moves, reads, deletes, etc.
- Expected keys:
RET
enter/open an item,q
quit and return to previous view,g
refresh view —i.e., ‘g’et new articles. RET
: Enter a group by pressing, well, the enter key.- Use
SPC
to open a group and automatically one first article there. - Use
C-u RET
to see all mail in a folder instead of just unread mail.
- Use
- Only groups/folders with unread mail will be shown, use
L/l
to toggle between listing all groups. SPC, DEL
to scroll forward and backward; orC-v, M-v
as always.G G
: Search mail at server side in the group buffer.- Limit search to particular folders/groups by marking them with
#
, or unmarking them withM-#
.
- Limit search to particular folders/groups by marking them with
/ /,a:
Filter mail according to subject or author; there are many other options, see §3.8 Limiting.d
: Mark an article as done, i.e., read it and it can be archived.!
: Mark an article as read, but to be kept around —e.g., you have not replied to it, or it requires more reading at a later time.This lets us read mail offline; cached mail is found at
~/News/cache/
.(setq gnus-use-cache 'use-as-much-cache-as-possible)
B m
: Move an article, in its current state, to another group —i.e., ‘label’ using Gmail parlance.- Something to consider doing when finished with an article.
To delete an article, simply move it to ‘trash’ —of course this will delete it in other mail clients as well. There is no return from trash.
Emails can always be archieved —never delete, maybe?
Anyhow,
B m Trash
is too verbose, let’s just uset
for “trash”:(bind-key "t" (lambda (N) (interactive "P") (gnus-summary-move-article N "[Gmail]/Trash")) gnus-summary-mode-map) ;; Orginally: t ⇒ gnus-summary-toggle-header
- Select and deselect many articles before
moving them by pressing
#
andM-#
, respectively, anywhere on the entry. - As usual, you can mark a region,
C-SPC
, then move all entries therein.
R, r
: Reply with sender’s quoted text in place, or without but still visible in an adjacent buffer.- Likewise
S W
orS w
to reply all, ‘wide reply’, with or without quoted text. C-c C-z
Delete everything from current position till the end.C-c C-e
Replace selected region with ‘[…]’; when omitting parts of quoted text.
- Likewise
- Press
m
to compose mail; orC-x m
from anywhere in Emacs to do so.C-c C-c
to send the mail.S D e
to resend an article as new mail: Alter body, subject, etc, beforeC-c C-f
to forward mail. sending.
C-c C-a
to attach a file; it’ll be embedded in the mail body as plaintext.- Press
o
on an attachment to save it locally.
- Press
Sometime mail contains useful reference material or may be a self-contained
task. Rather than using our inbox as a todo-list, we can copy the content of the
mail and store it away in our todos/notes files. Capturing below, is a way to,
well, capture ideas and notes without interrupting the current workflow. Below,
in the section on capturing, we define my/org-capture-buffer
which quickly
captures the contents of the current buffer as notes to store away. We use that
method in the article view of mail so that c
captures mail content with the
option to provide additional remarks, and C
to silently do so without additional
remarks.
(bind-key "c" #'my/org-capture-buffer gnus-article-mode-map)
;; Orginally: c ⇒ gnus-summary-catchup-and-exit
(bind-key "C"
(lambda (&optional keys)
(interactive "P") (my/org-capture-buffer keys 'no-additional-remarks))
gnus-article-mode-map)
;; Orginally: C ⇒ gnus-summary-cancel-article
Gnus’ default c
only enables a bad habit: Subscribing to stuff that you don’t
read, since you can mark all entries as read with one key. We now replace it
with a ‘c’apturing mechanism that captures the current message as a todo or note
for further processing. Likewise, the default C
is to cancel posting an article;
we replace it to be a silent capture: Squirrel away informative mail content
without adding additional remarks.
In order to get going quickly, using gmail2bbdb, let’s convert our Gmail contacts into a BBDB file —the Insidious Big Brother Database is an address-book application that we’ll use for E-mail; if you want to use it as a address-book application to keep track of contacts, notes, their organisation, etc, then consider additionally installing helm-bbdb which gives a nice menu interface.
- From the Gmail Contacts page, obtain a
contacts.vcf
file by clicking “More -> Export -> vCard format -> Export”. - Run command
M-x gmail2bbdb-import-file
and selectcontacts.vcf
; abbdb
file will be created in my Dropbox folder. - Press
C-x m
then begin typing a contact’s name and you’ll be queried about setting up BBDB, say yes.
(use-package gmail2bbdb
:custom (gmail2bbdb-bbdb-file "~/Dropbox/bbdb"))
(use-package bbdb
:demand t
:after company ;; The “com”plete “any”thig mode is set below in §Prose
:hook (message-mode . bbdb-insinuate-gnus)
(gnus-startup-hook . bbdb-insinuate-gnus)
:custom (bbdb-file gmail2bbdb-bbdb-file)
(bbdb-use-pop-up t) ;; allow popups for addresses
:config (add-to-list 'company-backends 'company-bbdb))
Here is an emacs-fu article on managing e-mail addressed with bbdb.
One can easily subscribe to an RSS feed in Gnus: Just press G R
in the group
buffer view, then follow the prompts. However, doing so programmatically is much
harder. Below is my heartfelt attempt at doing so —if you want a feed reader
in Emacs that “just works”, then elfeed is the way to go. When all is said and
done, the code below had me reading Gnus implementations and led me to conclude
that Gnus has a great key-based interface but a /poor programming interface —or
maybe I need to actually read the manual instead of frantically consulting
source code.
My homemade hack to getting tagged feeds programmatically into Gnus.
;; Always show Gnus items organised by topic.
(add-hook 'gnus-group-mode-hook 'gnus-topic-mode)
;; From Group view, press ^, then SPC on Gwene, then look for the site you want to follow.
;; If it's not there, add it via the web interface http://gwene.org/
(add-to-list 'gnus-secondary-select-methods '(nntp "news.gwene.org"))
;;
;; E.g., http://nullprogram.com/feed/ uses an Atom feed which Gnus does not
;; support natively. But it can be found on Gwene.
(setq my/gnus-feeds
;; topic title url
'(Emacs "C‘est La 𝒵" https://cestlaz.github.io/rss.xml
Emacs "Marcin Borkowski's Blog" http://mbork.pl?action=rss
Emacs "Howardism" http://www.howardism.org/rss.xml
Islam "Shia Islam Blogspot" http://welcometoshiaislam.blogspot.com/feeds/posts/default?alt=rss
Cats "Hedonistic Learning" http://www.hedonisticlearning.com/rss.xml
Cats "Functorial Blog" https://blog.functorial.com/feed.rss
Programming "Joel on Software" http://www.joelonsoftware.com/rss.xml
Haskell "Lysxia's Blog" https://blog.poisson.chat/rss.xml))
;; If fubared, then:
;; (ignore-errors (f-delete "~/News/" 'force) (f-delete "~/.newsrc.eld"))
;; Execute this after a Gnus buffer has been opened.
(progn
(use-package with-simulated-input)
(loop for (topic title url)
in (-partition 3 my/gnus-feeds)
;; url & topic are symbols, make them strings.
for url′ = (symbol-name url)
for topic′ = (symbol-name topic)
;; Avoid spacing issues by using a Unicode ghost space “ ”.
for title′ = (gnus-newsgroup-savable-name (s-replace " " " " title))
for input = (format "C-SPC C-a %s RET RET" title′)
do
; cl-letf* (((symbol-function 'insert) (lambda (x) nil))) ;; see the (undo) below.
;; Add the group
(with-simulated-input input
(gnus-group-make-rss-group url′))
;; Ensure it lives in the right topic category.
(if (equal 'no-such-topic (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=))
(push (list topic′ title′) gnus-topic-alist) ;; make topic if it doesnt exist
(setf (alist-get topic′ gnus-topic-alist 'no-such-topic nil #'string=)
(cons title′ (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=)))))
;; Acknowledgement
(message "Now switch into the GNUS group buffer, and refresh the topics; i.e., t t."))
;; The previous command performs an insert, since it's intended to be interactively
;; used; let's undo the insert.
; (undo-only)
;; (setq gnus-permanently-visible-groups ".*")
;;
;; Show topic alphabetically? The topics list is rendered in reverse order.
;; (reverse (cl-sort gnus-topic-alist 'string-lessp :key 'car))
Ironically, I’ve decide that “no, I do not want to see my blogs in Emacs” for
the same reasons I do not activelly use M-x eww
to browse the web in Emacs: I
like seeing the colours, fonts, and math symbols that the authours have labored
over to producing quality content. Apparently, I’m shallow and I’m okay with it
—but not that shallow, since I’m constantly pushing Emacs which looks ugly by
default but it’s unreasonably powerful.
Sometimes we have keybindings that share a common prefix, say C-c j
and C-c k
,
and we invoke them in an arbitrary sequence, it would be nice to invoke the
shared prefix only once thereby having:
C-c j C-c j C-c k C-c k M-3 C-c j M-5 C-c k | ≈ | C-c jjkk3j5k |
- The “hydra-zoom” example from the documentation really showcases this utility.
- After the prefix is supplied, all extensions are shown in a minibuffer.
;; Invoke all possible key extensions having a common prefix by
;; supplying the prefix only once.
(use-package hydra)
;; The standard syntax:
;; (defhydra hydra-example (global-map "C-c v") ;; Prefix
;; ;; List of triples (extension method description) )
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.
See Taking a tour of one’s edits below for a small and useful example.
Pop up a terminal, do some work, then close it using the same command.
Shell-pop uses only one key action to work: If the buffer exists, and we’re in
it, then hide it; else jump to it; otherwise create it if it doesn’t exit. Use
universal arguments, e.g., C-u 5 C-t
, to have multiple shells and the same
universal arguments to pop those shells up, but C-t
to pop them away.
(use-package shell-pop
:custom
;; This binding toggles popping up a shell, or moving cursour to the shell pop-up.
(shell-pop-universal-key "C-t")
;; Percentage for shell-buffer window size.
(shell-pop-window-size 30)
;; Position of the popped buffer: top, bottom, left, right, full.
(shell-pop-window-position "bottom")
;; Please use an awesome shell.
(shell-pop-term-shell "/bin/zsh"))
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:
brew install zsh
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
(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 ;-)
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
:demand t
;; 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")
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.
Let’s use an elementary diff system for backups.
(use-package backup-walker
:commands backup-walker-start)
In a buffer that corresponds to a file, invoke backup-walker-start
to see a
visual diff of changes between versions. 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.
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)
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.
diff-backup
compares a file with its backup or vice versa.
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.
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 batter 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
.
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 " (system-name)
"; Time " (emacs-init-time))))
Now my startup message is,
;; Welcome Musa Al-hassy! Emacs 26.1; Org-mode 9.3; System alhassy-air.local
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 (•̀ᴗ•́)و"))
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.
(find-file "~/Dropbox/todo.org")
(split-window-right) ;; C-x 3
(other-window 1) ;; C-x 0
(let ((enable-local-variables :all) ;; Load *all* locals.
(org-confirm-babel-evaluate nil)) ;; Eval *all* blocks.
(find-file "~/.emacs.d/init.org"))
There is the neat-looking emacs-dashboard package that provides an extensbile yet minimalist splash screen showing recent files, projects, and bookmarks.
Emacs’ default theme leaves much to be desired: It does not look sleek and shiny, which usually leaves first-timers with a poor, shallow, impression of the system.
Below we install a few themes that make Emacs look exquisite.
We cycle between the chosen themes with C-x t
.
M-x load-theme RET TAB
shows all themes, including built-in ones, that may be loaded.- Loading multiple themes results in their pallets mixed.
M-x disable-theme
to remove a theme from the current pallet.
;; Treat all themes as safe; no query before use.
(setf custom-safe-themes t)
;; Nice looking themes ^_^
(use-package solarized-theme)
(use-package doom-themes)
(use-package spacemacs-common
:ensure spacemacs-theme)
- The Doom Themes also look rather appealing.
- A showcase of many themes can be found here.
;; Infinite list of my commonly used themes.
(setq my/themes '(doom-solarized-light doom-vibrant spacemacs-light))
(setcdr (last my/themes) my/themes)
“C-x t” to toggle between the personal themes.
(cl-defun my/disable-all-themes (&key (new-theme (pop my/themes)))
"Disable all themes and load NEW-THEME, which defaults from ‘my/themes’."
(interactive)
(dolist (τ custom-enabled-themes)
(disable-theme τ))
(when new-theme (load-theme new-theme)))
(defalias 'my/toggle-theme #' my/disable-all-themes)
(global-set-key "\C-x\ 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.
The ‘modeline’ is a part near the bottom of Emacs that gives information about the current mode, as well as other matters —such as time & date, for example.
Let’s have it also show remaining battery life, coloured green if charging and coloured yellow otherwise. It is important to note that this package is no longer maintained. It works on my machine.
(setq display-time-day-and-date t)
(display-time)
;; (display-battery-mode -1)
;; Nope; let's use a fancy indicator …
(use-package fancy-battery
:diminish
:custom (fancy-battery-show-percentage t)
(battery-update-interval 15)
:config (fancy-battery-mode))
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.
;; Following two taken care of in the spaceline package, below.
;; (column-number-mode t)
;; (line-number-mode t)
(setq display-line-numbers-width-start t)
(global-display-line-numbers-mode t)
I may not use the spacemacs starter kit, since I find spacemacs to “hide things” from me —whereas Emacs “encourages” me to learn more—, however it is a configuration and I enjoy reading Emacs configs in order to improve my own setup. From Spacemacs I’ve adopted Helm for list completion, its sleek light & dark themes, and its modified powerline setup.
;; When using helm & info & default, mode line looks prettier.
(use-package spaceline
:custom (spaceline-buffer-encoding-abbrev-p nil)
(spaceline-line-column-p t) ;; Show “line-number : column-number” in modeline.
(powerline-default-separator 'arrow)
:config (require 'spaceline-config)
(spaceline-helm-mode)
(spaceline-info-mode)
(spaceline-emacs-theme))
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.
Let’s have the entire line containing the cursour be slightly highlighted.
;; Make it very easy to see the line with the cursor.
(global-hl-line-mode t)
Moreover, we reduce the mental strain of locating the cursour when navigation happens: When we switch windows or scroll, for example, we get a wave of light near the cursor.
(use-package beacon
:diminish
:config (setq beacon-color "#666600")
:hook ((org-mode text-mode) . beacon-mode))
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.
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, 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! )
Make top and bottom of screen flash when something unexpected happens thereby observing a warning message in the minibuffer. E.g., C-g, or calling an unbound key sequence, or misspelling a word.
(setq visible-bell 1)
Enable flashing mode-line on errors. On MacOS, this shows a caution symbol ^_^
A blinking cursor rushes me to type; let’s slow down. … Recentely I’m thinking that a blinking cursours prompts me to continue upwards and onwards.
(blink-cursor-mode 1)
As a laptop user, screen space is important, so let’s remove rarely used visual items.
(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
Highlight matching ‘parenthesis’ when near one of them.
(setq show-paren-delay 0)
(setq show-paren-style 'mixed)
(show-paren-mode)
Colour parens, and other delimiters, depending on their depth. Very useful for parens heavy languages like Lisp.
(use-package rainbow-delimiters
:disabled
:hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode))
For example:
(blue (purple (forest (green (yellow (blue))))))
There is a powerful package called ‘smartparens’ for working with pair-able
characters, but I’ve found it to be too much for my uses. Instead I’ll utilise
the lightweight package electric
, which Emacs provides out of the box.
(electric-pair-mode 1)
It supports, by default, ACSII pairs {}, [], ()
and Unicode ‘’, “”, ⟪⟫, ⟨⟩
.
When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed and considered as pairs. Let’s disassociate them from both notions.
;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition.
(setq electric-pair-inhibit-predicate
(lambda (c)
(or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c))))
;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’.
(modify-syntax-entry ?< "w<")
(modify-syntax-entry ?> "w>")
Adding Org-emphasise markers for pair completion —Disabled.
Let’s add the org-emphasises markers: If we select a word then press *
, it
becomes bold; likewise for /
for emphasise.
(setq electric-pair-pairs
'((?~ . ?~)
(?* . ?*)
(?/ . ?/)))
;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file.
;; Disable pairs when entering minibuffer
(add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0)))
;; Renable pairs when existing minibuffer
(add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1)))
I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous Org headers, for which the ‘completed closing pair’ must tiresomely be deleted.
We open a nifty file manager upon startup.
;; Sidebar for project file navigation
(use-package neotree
:config (global-set-key "\C-x\ d" 'neotree-toggle)
(setq neo-theme 'icons)) ;; Uses all-the-icons from § Booting Up
;; Open it up upon startup.
;; (neotree-toggle)
By default C-x d
invokes dired
, but I prefer neotree
for file
management.
⟨ Edit: As a naive user, this is what I thought; yet a year later, I’ve almost never used neotree. ⟩
Useful navigational commands include
U
to go up a directory.C-c C-c
to change directory focus;C-C c
to type the directory out.?
orh
to get help andq
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.
I really like my Helm-supported C-x b
, but the visial appeal of a tab bar for Emacs
is interesting. Let’s try it out and see how long this lasts —it may be like Neotree:
Something cute to show to others, but not as fast as the keyboard.
(use-package awesome-tab
:disabled
:quelpa (awesome-tab :fetcher git :url "https://github.com/manateelazycat/awesome-tab.git")
:config (awesome-tab-mode t))
;; Show me /all/ the tabs at once, in one group.
(defun awesome-tab-buffer-groups ()
(list (awesome-tab-get-group-name (current-buffer))))
It’s been less than three days and I’ve found this utility to be unhelpful, to me anyhow.
An alternative is centaur-tabs.
Let’s load the following package, which automatically resizes windows so that the window containing the cursor is the largest, according to the golden ratio. Consequently, the window we’re working with is nice and large yet the other windows are still readable.
(use-package golden-ratio
:disabled
:diminish golden-ratio-mode
:init (golden-ratio-mode 1))
After some time this got a bit annoying and I’m no longer using this.
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"))
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 (•̀ᴗ•́)و
Let’s start off by cleaning-up any accidental trailing whitespace and in other places upon save.
(add-hook 'before-save-hook 'whitespace-cleanup)
- Org-mode is a writer’s best friend; it’s large enough to deserve its own sections.
- See here for making whitespace visible; including spaces, tabs, and newlines
In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting paragraphs. This is a powerful form of “word wrap”.
(setq-default fill-column 80 ;; Let's avoid going over 80 columns
truncate-lines nil ;; I never want to scroll horizontally
indent-tabs-mode nil) ;; Use spaces instead of tabs
Certain variables are sensibly local to a buffer, and so setq
only alters their
value for one buffer. Using setq-default
we change a variable’s default value,
in every buffer.
;; Wrap long lines when editing text
(add-hook 'text-mode-hook 'turn-on-auto-fill)
(add-hook 'org-mode-hook 'turn-on-auto-fill)
;; Do not show the “Fill” indicator in the mode line.
(diminish 'auto-fill-function)
We may press M-q
to cleverly redistribute the line breaks within any paragraph,
thereby making it look better. With a prefix argument, it justifies it as well
—i.e., pads extra white space to make the paragraph appear rectangular.
Note that M-o M-s
centres a line of text ;-) Fun stuff!
Let’s enable “complete anything” mode —it ought to start in half a second and only need two characters to get going, which means word suggestions are provided and so I need only type partial words then tab to get the full word!
(use-package company
:diminish
:config
(global-company-mode 1)
(setq ;; Only 2 letters required for completion to activate.
company-minimum-prefix-length 2
;; Search other buffers for compleition candidates
company-dabbrev-other-buffers t
company-dabbrev-code-other-buffers t
;; Allow (lengthy) numbers to be eligible for completion.
company-complete-number t
;; M-⟪num⟫ to select an option according to its number.
company-show-numbers t
;; Edge of the completion list cycles around.
company-selection-wrap-around t
;; Do not downcase completions by default.
company-dabbrev-downcase nil
;; Even if I write something with the ‘wrong’ case,
;; provide the ‘correct’ casing.
company-dabbrev-ignore-case t
;; Immediately activate completion.
company-idle-delay 0))
;; It's so fast that we don't need a key-binding!
;; (global-set-key (kbd "C-c h") 'company-complete)
Note that M-/
goes through a sequence of completions. Besides the
arrow keys, we can also use C-
or M-
with n, p
to navigate the options. Note
that by default company mode does not support completion for phrases containing
hyphens —this can be altered, if desired.
Besides boring word completion, let’s add support for emojis.
(use-package company-emoji
:config (add-to-list 'company-backends 'company-emoji))
For example: 🥞 💻 🐵 ✉️😉 🐬 🌵.
➡️On a new line, write :
then any letter to have a tool-tip appear.
All emoji names are lowercase. ◀
The libraries emojify
and emojify-logos
provides cool items like =:haskell: :emacs:
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.
flyspell-prog-mode
enables spell checking for programming by only considering
comments and strings.
(use-package flyspell
:diminish
:hook ((prog-mode . flyspell-prog-mode)
(org-mode text-mode . flyspell-mode)))
Enabling fly-spell for text-mode enables it for org and latex modes since they derive from text-mode.
Flyspell needs a spell checking tool, which is not included in Emacs. We
install aspell
spell checker using, say, homebrew via brew install aspell
. Note
that Emacs’ ispell
is the interface to such a command line spelling utility.
(setq ispell-program-name "/usr/local/bin/aspell")
(setq ispell-dictionary "en_GB") ;; set the default dictionary
[Disabled] Allow spelling support for CamlCase words like “EmacsIsCool”.
(setq ispell-extra-args '("--sug-mode=ultra"
"--run-together"
"--run-together-limit=5"
"--run-together-min=2"))
Let us select a correct spelling merely by clicking on a word —for the rare days I have a mouse.
(eval-after-load "flyspell"
' (progn
(define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word)
(define-key flyspell-mouse-map [mouse-3] #'undefined)))
Colour incorrect works; default is an underline.
(global-font-lock-mode t)
(custom-set-faces '(flyspell-incorrect ((t (:inverse-video t)))))
Finally, save to user dictionary without asking:
(setq ispell-silently-savep t)
Let’s keep track of my personal word set by having it be in my version controlled
.emacs directory. Note that the default location is ~~/.[i|a]spell.DICT~ for
a specified dictionary DICT
.
(setq ispell-personal-dictionary "~/.emacs.d/.aspell.en.pws")
Nowadays, I very rarely write non-literate programs, but if I do I’d like to check spelling only in comments/strings. E.g.,
(add-hook 'c-mode-hook 'flyspell-prog-mode)
(add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode)
Use the thesaurus Emacs frontend Synosaurus to avoid unwarranted repetition.
(use-package synosaurus
:diminish synosaurus-mode
:init (synosaurus-mode)
:config (setq synosaurus-choose-method 'popup) ;; 'ido is default.
(global-set-key (kbd "M-#") 'synosaurus-choose-and-replace))
The thesaurus is powered by the Wordnet wn
tool, which can be invoked without an
internet connection!
;; (shell-command "brew cask install xquartz &") ;; Dependency
;; (shell-command "brew install wordnet &")
Let’s use Wordnet as a dictionary via the wordnut package.
(use-package wordnut
:bind ("M-!" . wordnut-lookup-current-word))
;; Use M-& for async shell commands.
Use M-↑,↓
to navigate dictionary results, and wordnut-search
for a new search.
An alternative to wordnut
is to use the lightweight define-word
package; which I
think is not ideal since it provides way less information.
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 😲
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
:config
(setq 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-^
(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.
Let’s write good!
(use-package writegood-mode
;; Load this whenver I'm composing prose.
:hook (text-mode org-mode)
;; Don't show me the “Wg” marker in the mode line
:diminish
;; Some additional weasel words.
:config
(--map (push it writegood-weasel-words)
'("some" "simple" "simply" "easy" "often" "easily" "probably"
"clearly" ;; Is the premise undeniably true?
"experience shows" ;; Whose? What kind? How does it do so?
"may have" ;; It may also have not!
"it turns out that"))) ;; How does it turn out so?
;; ↯ What is the evidence of highighted phrase? ↯
Inspired by Matt Might’s 3 shell scripts to improve your writing, or “My Ph.D. advisor rewrote himself in bash”, this Emacs interface emphasises, via underline, the following weaknesses in writing —so that I can fix them or decide that they are appropriate for the scenario.
Sentences that cut out the following problems may become stronger —by being more terse or precise.
- Weasel Words
-
Phrases that sound good without conveying information;
such as vague precision or subjective phrases.
E.g., a number of, surprisingly, very close.
It’s okay not to have exact details, but rather than “I don’t know” explain why not and what the next steps will be.
- Passive Voice
-
Phrases wherein interest is in the object experiencing an action,
rather than the subject that performs the action.
- Bad: The house was built by my father.
- Good: My father built this house.
Likewise, including relevant or explanatory information as in “X guarantees Y” is an improvement over “Y is guaranteed”.
Sometimes the subject really is irrelevant, such as “We did X” whereas “X happened” suffices.
👍 If the relevant subject is unclear and, also, the text reads better in the active, then change a phrase.
- Duplicated Words
- Occurrences of, say, “the the”.
Harder to catch manually, but easier mechanically ;-)
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.
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.
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!) )
Executing agda-mode setup
appends the following text to the .emacs
file.
Let’s put it here ourselves.
(load-file (let ((coding-system-for-read 'utf-8))
(shell-command-to-string "/usr/local/bin/agda-mode locate")))
I almost always want the agda-mode
input method —it’s like the TeX method, but better.
(require 'agda-input)
(setq default-input-method "Agda")
(toggle-input-method) ;; C-\
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 𝒮ℯ𝓉
.
(add-to-list 'agda-input-user-translations '("set" "𝒮ℯ𝓉"))
Better yet, as a loop:
(loop for item
in '(;; categorial ;;
("alg" "𝒜𝓁ℊ")
("split" "▵")
("join" "▿")
("adj" "⊣")
(";;" "﹔")
(";;" "⨾")
(";;" "∘")
;; lattices ;;
("meet" "⊓")
("join" "⊔")
;; residuals
("syq" "╳")
("over" "╱")
("under" "╲")
;; Z-quantification range notation ;;
;; e.g., “∀ x ❙ R • P” ;;
("|" "❙")
("with" "❙")
;; adjunction isomorphism pair ;;
("floor" "⌊⌋")
("lower" "⌊⌋")
("lad" "⌊⌋")
("ceil" "⌈⌉")
("raise" "⌈⌉")
("rad" "⌈⌉")
;; more (key value) pairs here
)
do (add-to-list 'agda-input-user-translations item))
Also some silly stuff:
;; Add to the list of translations using “emot” and the given, more specfic, name.
;; Whence, \emot shows all possible emotions.
(loop for emot
in `(;; angry, cry, why-you-no
("whyme" "ლ(ಠ益ಠ)ლ" "ヽ༼ಢ_ಢ༽ノ☂" "щ(゜ロ゜щ)")
;; confused, disapprove, dead, shrug
("what" "「(°ヘ°)" "(ಠ_ಠ)" "(✖╭╮✖)" "¯\\_(ツ)_/¯")
;; dance, csi
("cool" "┏(-_-)┓┏(-_-)┛┗(-_- )┓"
,(s-collapse-whitespace "•_•)
( •_•)>⌐■-■
(⌐■_■)"))
;; love, pleased, success, yesss
("smile" "♥‿♥" "(─‿‿─)" "(•̀ᴗ•́)و" "(งಠ_ಠ)ง"))
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
(agda-input-setup)
Note that the effect of Emacs unicode input could be approximated using
abbrev-mode
.
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 sizeC-x C--
decreases test sizeC-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.
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)
(move-text-default-bindings)
Subword movement lets us treat “EmacsIsAwesome” as three words
─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming
is common among coders. Now, for example, M-f
moves along each subword.
(global-subword-mode 1)
(diminish 'subword-mode)
Text selected with the mouse is automatically copied to clipboard.
(setq mouse-drag-copy-region t)
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)
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
.
(use-package smartscan
:config
(global-set-key (kbd "M-n") 'smartscan-symbol-go-forward)
(global-set-key (kbd "M-p") 'smartscan-symbol-go-backward)
(global-set-key (kbd "M-'") 'my/symbol-replace))
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.
The default symbol replacement is over-zealous in that it replaces sub-terms occurring as parts of larger words. Let’s do something about that.
(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))))
Also …
;; C-n, next line, inserts newlines when at the end of the buffer
(setq next-line-add-newlines t)
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ᵢ . |
This package allows us to move around the edit points of a buffer
without actually undoing anything. We even obtain a brief description
of what happend at each edit point.
This seems useful for when I get interrupted or lose my train of
thought: Just press C-c e p
to see what I did recently and where
—the “e” is for “e”dit.
;; Give me a description of the change made at a particular stop.
(use-package goto-chg
:init (setq glc-default-span 0))
(defhydra hydra-edits (global-map "C-c e")
("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.
Let’s get Org-mode setup so that we can quickly move between headings and
org-blocks —~n,p~ on heading starts and s-n,p
on blocks—, then let’s prettify
the leading stars of headings, Org’s formatting delimiters, and even its blocks
delimiters by making them less intrusive thereby ‘fading into the background’
and drawing minimal attention. This has been useful when promoting Org-mode by
sharing my screen with others.
Let’s obtain Org-mode 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 org
:ensure org-plus-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 repoistory.
- Use the
:ignore:
tag on headlines you’d like to have ignored, while not ignoring their content. - Use the
:noexport:
tag to omit a headline and its contents.
;; Replace the content marker, “⋯”, with a nice unicode arrow.
(setq org-ellipsis " ⤵")
;; Fold all source blocks on startup.
(setq org-hide-block-startup t)
;; Lists may be labelled with letters.
(setq org-list-allow-alphabetical t)
;; Avoid accidentally editing folded regions, say by adding text after an Org “⋯”.
(setq org-catch-invisible-edits 'show)
;; I use indentation-sensitive programming languages.
;; Tangling should preserve my indentation.
(setq org-src-preserve-indentation t)
;; Tab should do indent in code blocks
(setq org-src-tab-acts-natively t)
;; Give quote and verse blocks a nice look.
(setq org-fontify-quote-and-verse-blocks t)
;; Pressing ENTER on a link should follow it.
(setq org-return-follows-link t)
I rarely use tables, but here is a useful Org-Mode Table Editing Cheatsheet and a friendly tutorial.
Moreover, since I end up using org-mode most of the time, let’s make that the default mode.
(setq initial-major-mode 'org-mode)
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)
A worked out example can be obtained as follows: <g TAB
then C-c C-C
to make a nice
simple graph —the code for this is in the next section.
Some initial languages we want org-babel to support:
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(shell . t)
(python . t)
(haskell . t)
(ruby . t)
(ocaml . t)
(C . t) ;; Captial “C” gives access to C, C++, D
(dot . t)
(latex . t)
(org . t)
(makefile . t)))
;; Preserve my indentation for source code during export.
(setq org-src-preserve-indentation t)
;; The export process hangs Emacs, let's avoid this.
;; MA: For one reason or another, this crashes more than I'd like.
;; (setq org-export-in-background t)
More languages can be added using add-to-list
.
Let’s enable 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:
#
toggleCOMMENT
-ing for an org-header.s
toggles “narrowing” to a subtree; i.e., hide the rest of the document.If you narrow to a subtree then any export,
C-c C-e
, will only consider the narrowed detail.I/O
clock In/Out to the task defined by the current heading.- Keep track of your work times!
v
view agenda.
u
for jumping upwards to the parent heading.c
for cycling structure below current heading, orC
for cycling global structure.i
insert a new same-level heading below current heading.w
refile current heading; options list pops-up to select which heading to move it to. Neato!t
cycle through the available TODO states.^
sort children of current subtree; brings up a list of sorting options.n/p
for next/previous visible heading.f/b
for jumping forward/backward to the next/previous same-level heading.D/U
move a heading down/up.L/R
recursively promote (move leftwards) or demote (more rightwards) a heading.1,2,3
to mark a heading with priority, highest to lowest.
We can add our own speed keys by altering the org-speed-commands-user
association list variable.
Moreover, ?
to see a complete list of keys available.
(setq org-use-speed-commands t)
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.
;;
(use-package org
:bind (:map org-mode-map
("s-p" . org-babel-previous-src-block)
("s-n" . org-babel-next-src-block)
("s-e" . org-edit-src-code)
:map org-src-mode-map
("s-e" . org-edit-src-exit)))
Interestingly, s-l
is “goto line”.
C-RET, C-S-RET
make a new heading where the latter marks it as aTODO
.- By default
M-RET
makes it easy to work with existing list items, headings, tables, etc by creating a new item, heading, etc.
Usually we want a newline then we indent, let’s make that the default.
(add-hook 'org-mode-hook '(lambda ()
(local-set-key (kbd "<return>") 'org-return-indent))
(local-set-key (kbd "C-M-<return>") 'electric-indent-just-newline))
Notice that I’ve also added another kind of return, for when I want to break-out of the indentation approach and start working at the beginning of the line.
In summary:
key | method | behaviour |
---|---|---|
<return> | org-return-indent | Newline with indentation |
M-<return> | org-meta-return | Newline with new org item |
C-M-<return> | electric-indent-just-newline | Newline, cursor at start |
C-<return> | org-insert-heading-respect-content | New heading after current content |
C-S-<return> | org-insert-todo-heading-respect-content | Ditto, but with a TODO marker |
On an org-heading, C-a
goes to after the star, heading markers.
To use speed keys, run C-a C-a
to get to the star markers.
C-e
goes to the end of the heading, not including the tags.
(setq org-special-ctrl-a/e t)
C-k
no longer removes tags, if activated in the middle of a heading’s name.
(setq org-special-ctrl-k t) ;; MA: Does not work …!
When you yank a subtree and paste it alongside a subtree of depth ‘d’, then the yanked tree’s depth is adjusted to become depth ‘d’ as well. If you don’t want this, then refile instead of copy-pasting.
(setq org-yank-adjusted-subtrees t)
;; org-mode math is now highlighted ;-)
(setq org-highlight-latex-and-related '(latex))
;; Hide the *,=,/ markers
(setq org-hide-emphasis-markers t)
;; (setq org-pretty-entities t)
;; to have \alpha, \to and others display as utf8
;; http://orgmode.org/manual/Special-symbols.html
The following is now disabled —it makes my system slower than I’d like.
;; Let's set inline images.
(setq org-display-inline-images t)
(setq org-redisplay-inline-images t)
(setq org-startup-with-inline-images "inlineimages")
;; Automatically convert LaTeX fragments to inline images.
(setq org-startup-with-latex-preview t)
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.
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 " / "))
(defun my/org-goto-line (line)
"Go to the indicated line, unfolding the parent Org header.
Implementation: Go to the line, then look at the 1st previous
org header, now we can unfold it whence we do so, then we go
back to the line we want to be at.
"
(interactive "nEnter line: ")
(goto-line line)
(org-previous-visible-heading 1)
(org-cycle)
(goto-line line))
(defun my/org-fold-current-subtree-anywhere-in-it ()
"Hide the current heading, while being anywhere inside it."
(interactive)
(save-excursion
(org-narrow-to-subtree)
(org-shifttab)
(widen)))
(add-hook 'org-mode-hook '(lambda ()
(local-set-key (kbd "C-c C-h") 'my/org-fold-current-subtree-anywhere-in-it)))
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. His setup also accounts for
quotes.
⟪ Incantation Omitted —Visit Rasmus Roulund’s site & copy-paste it, if you wish ⟫
(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.")
(loop for pair in '(("<=" . ?≤) (">=" . ?≥)
("->" . ?→) ("-->". ?⟶) ;; threading operators
("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒
("#+end_example" . ?⇐)) ;; ⇐
do (push pair my/prettify-alist))
(loop for hk in '(text-mode-hook prog-mode-hook org-mode-hook)
do (add-hook hk (lambda ()
(setq prettify-symbols-alist
(append my/prettify-alist prettify-symbols-alist)))))
See “Mathematical Notation in Emacs” for how such prettifications can make verbose (Python) scripts much more readable by employing more economical disguises.
A nice sanity:
;; Un-disguise a symbol when cursour is inside it or at the right-edge of it.
(setq prettify-symbols-unprettify-at-point 'right-edge)
In org-mode we type <X TAB
to obtain environment templates, such as <s
for
source blocks or <q
for quote blocks. It seems recent changes to the org-mode
structure template expansion necessitate explicitly loading org-tempo
.
(require 'org-tempo)
To insert source blocks with the assistance of a pop-up: C-c C-v d
;-)
Perhaps more usefully, invoking within a source block splits it up into two
separate blocks! Moreover, if invoked on a selected region, it puts the region
into a new code block! Wow!
C-c C-,
refers toorg-insert-structure-template
, which provides non-source blocks, such as quote<q
, comment<C
, center<c
, notes<n
, examples<e
, and<l
and<h
and<a
for LaTeX and HTML and ASCII export blocks.<X
allows you to obtain the org-block assigned to shortcutX
.- The contents of comment blocks are ignored upon export.
C-c C-v C-d
andC-c C-v d
refer to theorg-babel-demarcate-block
, which provides source blocks.
We shall improve upon this system below using snippets. |
E.g., s_em TAB
to obtain an org-src block marked with emacs-lisp
as the
language. This saves us a few key strokes.
In this section we consider the Org-mode exporters for PDFs and HTMLs. For example, we account for LaTeX citations and reliable HTML anchors.
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!
;; Files to look at when no “╲bibliography{⋯}” is not present in a file.
;; Most useful for non-LaTeX files.
(setq reftex-default-bibliography '("~/thesis-proposal/papers/References.bib"))
(setq bibtex-completion-bibliography (car reftex-default-bibliography))
(use-package org-ref
:config (setq 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.
Execute the following for bib ref 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.
Upon HTML export, each tree heading is assigned an ID to be used for hyperlinks.
Default IDs are something like org1957a9d
, which does not endure the test of time:
Re-export will produce a different id. Here’s a rough snippet to generate
IDs from headings, by replacing spaces with hyphens, for headings without IDs.
(defun my/ensure-headline-ids (&rest _)
"Org trees without a
All non-alphanumeric characters are cleverly replaced with ‘-’.
If multiple trees end-up with the same id property, issue a
message and undo any property insertion thus far.
E.g., ↯ We'll go on a ∀∃⇅ adventure
↦ We'll-go-on-a-adventure
"
(interactive)
(let ((ids))
(org-map-entries
(lambda ()
(org-with-point-at (point)
(let ((id (org-entry-get nil "CUSTOM_ID")))
(unless id
(thread-last (nth 4 (org-heading-components))
(s-replace-regexp "[^[:alnum:]']" "-")
(s-replace-regexp "-+" "-")
(s-chop-prefix "-")
(s-chop-suffix "-")
(setq id))
(if (not (member id ids))
(push id ids)
(message-box "Oh no, a repeated id!\n\n\t%s" id)
(undo)
(setq quit-flag t))
(org-entry-put nil "CUSTOM_ID" id))))))))
;; Whenever html & md export happens, ensure we have headline ids.
(advice-add 'org-html-export-to-html :before 'my/ensure-headline-ids)
(advice-add 'org-md-export-to-markdown :before 'my/ensure-headline-ids)
One may then use [[#my-custom-id]]
to link to the entry with CUSTOM_ID
property my-custom-id
.
Interestingly, org-set-property
, C-c C-x p
, lets us insert a property
from a selection of available ones, then we’ll be prompted for a value
for it from a list of values you’ve used elsewhere. This is useful for
remaining consistent for when trees share similar properties.
(defun my/org-drawer-format (name contents)
"Export to HTML the drawers named with prefix ‘fold_’, ignoring case.
The resulting drawer is a ‘code-details’ and so appears folded;
the user clicks it to see the information therein.
Henceforth, these are called ‘fold drawers’.
Drawers without such a prefix may be nonetheless exported if their
body contains ‘:export: t’ ---this switch does not appear in the output.
Thus, we are biased to generally not exporting non-fold drawers.
One may suspend export of fold drawers by having ‘:export: nil’
in their body definition.
Fold drawers naturally come with a title.
Either it is specfied in the drawer body by ‘:title: ⋯’,
or otherwise the drawer's name is used with all underscores replaced
by spaces.
"
(let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents))
(fold? (s-prefix? "fold_" name 'ignore-case))
(export? (string-match ":export:\s+t" contents))
(not-export? (string-match ":export:\s+nil" contents))
(title′ (and (string-match ":title:\\(.*\\)\n" contents)
(match-string 1 contents))))
;; Ensure we have a title.
(unless title′ (setq title′ (s-join " " (cdr (s-split "_" name)))))
;; Output
(cond
((and export? (not fold?)) contents′)
(not-export? nil)
(fold?
(thread-last contents′
(replace-regexp-in-string ":title:.*\n" "")
(format "<details class=\"code-details\"> <summary> <strong>
<font face=\"Courier\" size=\"3\" color=\"green\"> %s
</font> </strong> </summary> %s </details>" title′))))))
(setq org-html-format-drawer-function 'my/org-drawer-format)
With the following invocations we only see the odd indexed ‘hello’s, where the latter two are folded up.
:this-drawer-is-exported:
:export: t
hello 1
:End:
:this-drawer-is-NOT-exported:
hello 2
:End:
:fold_This_drawer_has_a_title_in_the_body:
:title: I am the drawer title 0
hello 3
:End:
:fold_This_drawer_is_NOT_exported:
:title: Why are we here?
:export: nil
hello 4
:End:
:fold_I_am_the_drawer_title_1:
hello 5
:End:
I doubt I could show an example in the Github README, since no HTML export is happening using my setup. In case you’re reading this on my blog, which has exported HTML. Here’s the example: Now that I’ve written this, I’m thinking it may have been preferably to use an org-block…?
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
:config (setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"))
For example, execute, C-x C-e
after the closing parenthesis of, the
following block to see an example slide-deck (─‿‿─)
(progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org")
(switch-to-buffer (find-file "Trying_out_reveal.org"))
(org-reveal-export-to-html-and-browse))
Org-mode exporting, C-c C-e
, now includes an option R
for such reveal slide decks.
Two dimensional slides may be a bit new to some people, so I like to
give viewers an option, in tiny font, to view the slide-deck
continuously and remind them that ?
provides useful shortcuts.
(setq org-reveal-title-slide "<h1>%t</h1> <h3>%a</h3>
<font size=\"1\">
<a href=\"?print-pdf&showNotes=true\">
⟪ Flattened View ; Press <code>?</code> for Help ⟫
</a>
</font>")
One should remove the &showNotes=true
if they do not want to include
speaker notes in the flattened view.
Within the flatenned view, one may wish to CTRL/CMD+P
then save the
resulting PDF locally.
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 toggleM-x read-only-mode
to edit the text, say for notes or deletions, as you read! No need to copy-paste.
It’s hard to estimate how long a task takes if you don’t keep ‘clock-in and clock-out’ of tasks. We can ‘capture’ todos right in the middle of a task without context-switching; e.g., no opening a todos file! After some reflection on the relative importance of the tasks, we can schedule them into our ‘agenda’.
Let’s do this!
⟪ This section is based on a dated, yet delightful, tutorial of the same title by John Wiegley. ⟫
We want a day-planner with the following use:
- “Mindlessly” & rapidly create new tasks.
- Schedule and archive tasks at the end, or start, of the work day.
- Glance at a week’s tasks, shuffle if need be.
- Prioritise the day’s tasks. Aim for ≤15 tasks.
- Progress towards completion of
A
tasks by documenting work completed. - Repeat! During the day, if anything comes up, capture it and intentionally forget about it.
Capture lets me quickly make notes & capture ideas, with associated reference material, without any interruption to the current work flow. Without losing focus on what you’re doing, quickly jot down a note of something important that just came up.
E.g., I have a task, or something I wish to note down, rather than opening
some file, then making a heading, then writing it; instead, I press
C-c c t
and a pop-up appears, I make my note, and it disappears —with my
notes file(s) now being altered! Moreover, by default it provides a timestamp
and a link to the file location where I made the note —helpful for tasks, tickets,
to be tackled later on.
;; Location of my todos/notes file
(setq org-default-notes-file "~/Dropbox/todo.org")
;; “C-c c” to quickly capture a task/note
(define-key global-map "\C-cc" #'my/org-capture) ;; See below.
By default we only get a ‘tasks’ form of capture, let’s add some more.
(cl-defun my/make/org-capture-template
(shortcut heading &optional (no-todo nil) (description heading) (scheduled t))
"Quickly produce an org-capture-template.
After adding the result of this function to ‘org-capture-templates’,
we will be able perform a capture with “C-c c ‘shortcut’”
which will have description ‘description’.
It will be added to the tasks file under heading ‘heading’.
‘no-todo’ omits the ‘TODO’ tag from the resulting item; e.g.,
when it's merely an interesting note that needn't be acted upon.
Default for ‘description’ is ‘heading’. Default for ‘no-todo’ is ‘nil’.
Scheduled items appear in the agenda; true by default.
The target is ‘file+headline’ and the type is ‘entry’; to see
other possibilities invoke: C-h o RET org-capture-templates.
The “%?” indicates the location of the Cursor, in the template,
when forming the entry.
"
`(,shortcut ,description entry
(file+headline org-default-notes-file ,heading)
,(concat "*" (unless no-todo " TODO") " %?\n"
(when nil ;; this turned out to be a teribble idea.
":PROPERTIES:\n:"
(if scheduled
"SCHEDULED: %^{Any time ≈ no time! Please schedule this task!}t"
"CREATED: %U")
"\n:END:") "\n\n ")
:empty-lines 1 :time-prompt t))
(setq org-capture-templates
(loop for (shortcut heading)
in (-partition 2 '("t" "Tasks, Getting Things Done"
"r" "Research"
"2" "2FA3"
"m" "Email"
"e" "Emacs (•̀ᴗ•́)و"
"i" "Islam"
"b" "Blog"
"a" "Arbitrary Reading and Learning"
"l" "Programming Languages"
"p" "Personal Matters"))
collect (my/make/org-capture-template shortcut heading)))
;; For now, let's automatically schedule items a week in advance.
;; TODO: FIXME: This overwrites any scheduling I may have performed.
(defun my/org-capture-schedule ()
(org-schedule nil "+7d"))
(add-hook 'org-capture-before-finalize-hook 'my/org-capture-schedule)
For now I capture everything into a single file. One would ideally keep separate client, project, information in its own org file.
- Looking at
my/make/org-capture-template
, one notices that capture actually lets you add any type of item to any file.
Let’s also ensure TODO-s respect hierarchical structure.
;; Cannot mark an item DONE if it has a TODO child.
;; Conversely, all children must be DONE in-order for a parent to be DONE.
(setq org-enforce-todo-dependencies t)
Where am I currently capturing?
- During meetings, when a nifty idea pops into my mind, I quickly capture it.
- I’ve found taking my laptop to meetings makes me an active listener and I get much more out of my meetings since I’m taking notes.
- Through out the day, as I browse the web, read, and work; random ideas pop-up, and I capture them indiscriminately.
- I envision that for a phone call, I would open up a capture to make note of what the call entailed so I can review it later.
- Anywhere you simply want to make a note, for the current heading, just press
C-c C-z
. The notes are just your remarks along with a timestamp; they are collected at the top of the tree, under the heading.;; Ensure notes are stored at the top of a tree. (setq org-reverse-note-order nil)
Yet another place to capture content is from mail, such as for reference material, or self-contained tasks. See above for this discussion.
(cl-defun my/org-capture-buffer (&optional keys no-additional-remarks
(heading-regexp "Subject: \\(.*\\)"))
"Capture the current [narrowed] buffer as a todo/note.
This is mostly intended for capturing mail as todo tasks ^_^
When NO-ADDITIONAL-REMARKS is provided, and a heading is found,
then make and store the note without showing a pop-up.
This is useful for when we capture self-contained mail.
The HEADING-REGEXP must have a regexp parenthesis construction
which is used to obtain a suitable heading for the resulting todo/note."
(interactive "P")
(let* ((current-content (substring-no-properties (buffer-string)))
(heading (progn (string-match heading-regexp current-content)
(or (match-string 1 current-content) ""))))
(org-capture keys)
(insert heading "\n\n\n\n" (s-repeat 80 "-") "\n\n\n" current-content)
;; The overtly verbose conditions are for the sake of clarity.
;; Moreover, even though the final could have “t”, being explicit
;; communicates exactly the necessary conditions.
;; Being so verbose leads to mutual exclusive clauses, whence order is irrelevant.
(cond
((s-blank? heading)
(beginning-of-buffer) (end-of-line))
((and no-additional-remarks (not (s-blank? heading)))
(org-capture-finalize))
((not (or no-additional-remarks (s-blank? heading)))
(beginning-of-buffer) (forward-line 2) (indent-for-tab-command)))))
With that in-hand, we use a wrapper to org-capture
to make use of it.
(defun my/org-capture (&optional prefix keys)
"Capture something!
C-c c ⇒ Capture something; likewise for “C-uⁿ C-c c” where n ≥ 3.
C-u C-c c ⇒ Capture current [narrowed] buffer.
C-u 5 C-c c ⇒ Capture current [narrowed] buffer without adding additional remarks.
C-u C-u C-c c ⇒ Goto last note stored."
(interactive "p")
(case prefix
(4 (my/org-capture-buffer keys))
(5 (my/org-capture-buffer keys :no-additional-remarks))
(t (org-capture prefix keys))))
- Org-protocol is a way to create capture notes in org-mode from other applications.
Anyhow…
Isn’t it great that we can squirrel away info into some default location then immediately return to what we were doing before —with speed & minimal distraction! ♥‿♥ Indeed, if our system for task management were slow then we may not produce tasks and so forget them altogether! щ(゜ロ゜щ)
- Entering tasks is a desirably impulsive act; do not make any further
scheduling considerations.
The next step, the review stage occurring at the end or the start of the workday, is for processing.
The reason for this is that entering new tasks should be impulsive, not reasoned. Your reasoning skills are required for the task at hand, not every new tidbit. You may even find that during the few hours that transpire between creating a task and categorizing it, you’ve either already done it or discovered it doesn’t need to be done at all! —John Wiegley
When my computer isn’t handy, I’ll make a note on my phone then transfer it later.
At a later time, a time of reflection, we go to our tasks list and actually
schedule time to get them done by C-c C-s
then pick a date by entering a number
in the form +𝓃
to mean that task is due 𝓃
days from now.
- Tasks with no due date are ones that “could happen anytime”, most likely no time at all.
- At least schedule tasks reasonably far off in the future, then reassess when the time comes.
- An uncompleted task is by default rescheduled to the current day, each day, along with how overdue it is.
- Aim to consciously reschedule such tasks!
With time, it will become clear what is an unreasonable day verses what is an achievable day.
Repeat tasks by a repeater such as ‘+1m’ or ‘+7d’ in their timestamps; e.g.,
DEADLINE: <2005-10-01 Sat +1m>.
A ‘project’ is a task that has multiple steps, each as a checkbox item. It can
be given a percentage marker to show progress: Place [%]
after its name, then
press C-c #
on the name to see a completion percentage —press C-c C-c
on a
checkbox item to toggle its completion state.
Let’s keep track of how many times, and when, we have pushed events to other dates.
;; Add a note whenever a task's deadline or scheduled date is changed.
(setq org-log-redeadline 'time)
(setq org-log-reschedule 'time)
The next day we begin our work, we press C-c a a
to see the
scheduled tasks for this week —~C-c C-s~ to re-schedule the
task under the cursor and r
to refresh the agenda.
(define-key global-map "\C-ca" 'org-agenda)
- Show the next 𝓃 days schedule ⇐
C-u 𝓃 C-c a a
.
The next section, Super Agenda, will discuss acting on entries in the agenda buffer.
After having seen our tasks for the week, we press d
to enter daily view
for the current day. Now we decide whether the items for today are
A
: of high urgency & important; B
: of moderate urgency & importance; or
C
: Pretty much optional, or very quick or fun to do.
A
tasks should be both important and urgently done on the day they were scheduled.- Such tasks should be relatively rare!
- If you have too many, you’re anxious about priorities and rendering priorities useless.
C
tasks can always be scheduled for another day without much worry.- Act! If the thought of rescheduling causes you to worry, upgrade it to a
B
orA
.
- Act! If the thought of rescheduling causes you to worry, upgrade it to a
- As such, most tasks will generally be priority
B
: Tasks that need to be done, but the exact day isn’t as critical as with anA
task. These are the “bread and butter” tasks that make up your day to day life.
On a task item, or any org-heading, press ,
then one of A, B, C
to set its
priority. Then r
to refresh.
Since A
tasks are the important and urgent ones, if you do all of the A
tasks and
nothing else today, no one would suffer. It’s a good day (─‿‿─).
There should be no scheduling nor prioritising at this stage. You should not be touching your tasks file until your next review session: Either at the end of the day or the start of the next.
- Leverage priorities! E.g., When a full day has several
C
tasks, reschedule them for later in the week without a second thought.- You’ve already provided consideration when assigning priorities.
My workflow states are described in the section
Workflow States and contain states: TODO, STARTED, WAITING, ON_HOLD, CANCELLED, DONE
.
- Tasks marked
WAITING
are ones for which we are awaiting some event, like someone to reply to our query. As such, these tasks can be rescheduled until I give up or the awaited event happens —in which case I go toSTARTED
and document the reply to my query. - The task may be put off indefinitely with
ON_HOLD
, or I may choose never to do it withCANCELLED
. Along withDONE
, these three mark a task as completed and so it needn’t appear in any agenda view.
I personally clock-in and clock-out of tasks —keep reading—, where upon clocking-out I’m prompted for a note about what I’ve accomplished so far. Entering a comment about what I’ve done, even if it’s very little, feels like I’m getting something done. It’s an explicit marker of progress.
In the past, I would make a “captain’s log” at the end of the day, but that’s like commenting code after it’s written, I didn’t always feel like doing it and it wasn’t that important after the fact. The continuous approach of noting after every clock-out is much more practical, for me at least.
During the review state, when a task is completed, ‘archive’ it with C-c C-x
C-s
: This marks it as done, adds a time stamp, and moves it to a local
*.org_archive
file. What was our ‘to do’ list becomes a ‘ta da’ list showcasing
all we have done (•̀ᴗ•́)و
Archiving keeps task lists clutter free, but unlike deletion it allows us,
possibly rarely, to look up details of a task or what tasks were completed in a
certain time frame —which may be a motivational act, to see that you have
actually completed more than you thought, provided you make and archive tasks
regularly. We can use M-x org-search-view
to search an org file and the archive
file too, if we enable it so.
;; C-c a s ➩ Search feature also looks into archived files.
;; Helpful when need to dig stuff up from the past.
(setq org-agenda-text-search-extra-files '(agenda-archives))
;; Invoking the agenda command shows the agenda and enables
;; the org-agenda variables.
(org-agenda "a" "a") ;; ➩ Show my agenda upon Emacs startup.
Let’s install some helpful views for our agenda.
C-c a c
: See completed tasks at the end of the day and archive them.;; Pressing ‘c’ in the org-agenda view shows all completed tasks, ;; which should be archived. (add-to-list 'org-agenda-custom-commands '("c" todo "DONE|ON_HOLD|CANCELLED" nil))
C-c a u
: See unscheduled, undeadlined, and undated tasks in my todo files. Which should then be scheduled or archived.(add-to-list 'org-agenda-custom-commands '("u" alltodo "" ((org-agenda-skip-function (lambda () (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp "\n]+>"))) (org-agenda-overriding-header "Unscheduled TODO entries: "))))
At the end of the day, let’s schedule at least 3 things that must be done the next day; i.e., have priority =A=.
The agenda dispatch menu, C-c a
, has options for displaying tasks. One may add
new ways to view tasks by altering the org-agenda-custom-commands
variable
—e.g., above we added two, one for completed tasks and one for unscheduled
tasks.
Let’s setup the basics of our agenda.
;; List of all the files & directories where todo items can be found. Only one
;; for now: My default notes file.
(setq org-agenda-files (list org-default-notes-file))
;; Display tags really close to their tasks.
(setq org-agenda-tags-column -10)
;; How many days ahead the default agenda view should look
(setq org-agenda-span 'week) ;; May be any number.
;; How many days early a deadline item will begin showing up in your agenda list.
(setq org-deadline-warning-days 14)
;; In the agenda view, days that have no associated tasks will still have a line showing the date.
(setq org-agenda-show-all-dates t)
;; Scheduled items marked as complete will not show up in your agenda view.
(setq org-agenda-skip-scheduled-if-done t)
(setq org-agenda-skip-deadline-if-done t)
The agenda view, like nearly all Emacs entities, is interactive:
-
𝓃 f,b
⇒ Look forward at next week’s agenda, or backward to a previous week.- The optional
$𝓃$ means do the action𝓃
-many times; it defaults to 1.
- The optional
-
w, d
⇒ toggle week view, or day view; usev
to see possible views.- E.g.,
C-u 2017 v y
shows us the specific year 2017.
- E.g.,
-
𝓃 n,p
to navigate to next and previous entries. -
t
⇒ cycle TODO state of the current entry. -
±
⇒ cycle priority state. -
𝓃 S-⇆
⇒ Shift date time by$𝓃$ days; 1 day by default. -
C-c C-s
⇒ Reschedule an entry; prefix it withC-u
to remove a scheduled entry.- Repeat tasks by a repeater such as ‘+1m’ or ‘+7d’
in their timestamps; e.g.,
DEADLINE: <2005-10-01 Sat +1m>.
-
s
⇒ save all agenda buffers; i.e., save the org-files where the agenda items live. -
g
⇒ Rebuild agenda according to any changes made thus far. -
F
⇒ Toggle ‘follow mode’: As you go up/down entries, you can see their details in an adjacent window.-
SPC
⇒ Show details of a single entry in other window; stay in Agenda.
-
-
RET, TAB
⇒ Go to the current entry in the current window or in a new adjacent window, so as to alter task details.
The agenda view ––even in the 7-days-at-a-time view–– will always begin on the current day. This is important, since while using org-mode as a day planner, you never want to think of days gone past. That’s something you do in other ways, such as when reviewing completed tasks.
(setq org-agenda-start-on-weekday nil)
Instead of having the day’s tasks all in one field, org-super-agenda allows us
to use predicates to group entries together; e.g., by considering an entry’s
:tags:
or its priority level. Since I’m placing all my tasks in a single file,
under appropriate parent headings, I want entries to be shown according to their
parent heading. Of-course, the top-most grouping, the important tasks, should be
pulled out of their group and placed at the top.
(use-package org-super-agenda
:after origami-mode
:hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB.
:bind (:map org-super-agenda-header-map ("<tab>" . origami-toggle-node))
:config
(org-super-agenda-mode)
(setq org-super-agenda-groups
'((:name "Important" :priority "A")
(:name "Personal" :habit t)
;; For everything else, nicely display their heading hierarchy list.
(:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path)))))))
- Origami mode —see below in Text Folding with Origami-mode—
works well with super-agenda. Just
M-x origami-mode
thenC-c f
to enable the folding hydra.
The org-super-agenda homepage shows complex configurations and pleasant screenshots contrasting with and without the system. E.g., you can change how entries in particular headings are displayed and coloured.
Automating Pomodoro —“Commit for only 25 minutes!”
Effort estimates are for an entire task. Yet, sometimes it’s hard to even get started on some tasks.
- The code below ensures a 25 minute timer is started whenever clocking in happens.
- The timer is in the lower right of the modeline.
- When the timer runs out, we get a notification.
- We may have the momentum to continue on the difficult task, or clock-out and take a break after documenting what was accomplished.
;; Tasks get a 25 minute count down timer
(setq org-timer-default-timer 25)
;; Use the timer we set when clocking in happens.
(add-hook 'org-clock-in-hook
(lambda () (org-timer-set-timer '(16))))
;; unless we clocked-out with less than a minute left,
;; show disappointment message.
(add-hook 'org-clock-out-hook
(lambda ()
(unless (s-prefix? "0:00" (org-timer-value-string))
(message-box "The basic 25 minutes on this difficult task are not up; it's a shame to see you leave."))
(org-timer-stop)))
Note that this does not conflict with the total effort estimate for the task.
⟨ I’m told there’s a package already made for this —maybe I need to stop writing good, and do more searches; then again, I’ve learned a lot by writing code. ⟩
Thus far I’ve made it easy to quickly capture ideas and tasks, not so much on the analysis phase:
- What was accomplished today?
- What are some notably bad habits? Good habits?
- What are some future steps?
Rather than overloading the capture mechanism for such thoughts, let’s employ
org-journal
—journal entries are stored in files such as journal/20190407
,
where the file name is simply the date, or only one file per year as I’ve set it
up below. Each entry is the week day, along with the date, then each child tree
is an actual entry with a personal title preceded by the time the entry was
made. Unlike capture and its agenda support, journal ensures entries are
maintained in chronological order with calendar support.
Since org files are plain text files, an entry can be written anywhere and later ported to the journal. Or, written directly in the journal file if we add the necessary Org-header: Asterisks and time.
The separation of concerns is to emphasise the capture stage as being quick and relatively mindless, whereas the journaling stage as being mindful. Even though we may utilise capture to provide quick support for including journal entries, I have set my journal to be on a yearly basis —one file per year— since I want to be able to look at previous entries when making the current entry; after all, it’s hard to compare and contrast easily unless there’s multiple entries opened already.
As such, ideally at the end of the day, I can review what has happened, and what has not, and why this is the case, and what I intend to do about it, and what problems were encountered and how they were solved —in case the problem is encountered again in the future. Consequently, if I encounter previously confronted situations, problems, all I have to do is reread my journal to get an idea of how to progress. Read more about the importance of reviewing your day on a daily basis.
Moreover, by journaling with Org on a daily basis, it can be relatively easy to produce a report on what has been happening recently, at work for example. I’d like to have multiple journals, for work and for personal life, as such I will utilise a prefix argument to obtain my work specific entries.
Anyhow, the setup:
(defun my/org-journal-new-entry (prefix)
"Open today’s journal file and start a new entry.
With a prefix, we use the work journal; otherwise the personal journal."
(interactive "P")
(-let [org-journal-file-format (if prefix "Work-%Y-%m-%d" org-journal-file-format)]
(org-journal-new-entry nil)
(org-mode)
(org-show-all)))
(use-package org-journal
;; C-u C-c j ⇒ Work journal ;; C-c C-j ⇒ Personal journal
:bind (("C-c j" . my/org-journal-new-entry))
:config
(setq org-journal-dir "~/Dropbox/journal/"
org-journal-file-type 'yearly
org-journal-file-format "Personal-%Y-%m-%d"))
Bindings available in org-journal-mode
, when journaling:
C-c C-j
: Insert a new entry into the current journal file.- Note that keys for
org-journal-new-entry
shadow those fororg-goto
.
- Note that keys for
C-c C-s
: Search the journal for a string.- Note that keys for
org-journal-search
shadow those fororg-schedule
.
- Note that keys for
All journal entries are registered in the Emacs Calendar. To see available
journal entries do M-x calendar
. Bindings available in the calendar-mode:
j
: View an entry in a new buffer.i j
: ‘I’nsert a new ‘j’ournal entry into the day’s file.f w/m/y/f/F
: ‘F’ind, search, in all entries of the current week, month, year, all of time, of in all entries in the future.
Sometimes it can be tough to journal, but filling in a template can be a way to
get started. Later on, we will setup Snippets —Template Expansion which will
allow us to write journal_guided
then TAB
to obtain the template below. Each $𝓃
indicates a position that we may input text, after which we TAB
to move to next
location.
Just like the undo-tree
setup at the start of this read, we use a noweb-ref
to present this template in a natural position; then later when template
expansion it setup, we request it to be tangled.
** journal_guided: Introspection & Growth
I'm writing from ${1:location}.
Gut answer, today I feel ${2:scale}/10.
⇒ ${3:Few words or paragraphs to explain what's on your mind.}
${4: All things which cause us to groan or recoil are part of the tax of
life. These things you should never hope or seek to escape. Life is a battle,
and to live is to fight.
⟨ Press TAB once you've read this mantra. ⟩
$(when yas-moving-away-p "")
}
`(progn
(eww "https://www.dailyinspirationalquotes.in/")
(sit-for 2) (when nil let eww load)
(read-only-mode -1)
(goto-line 52)
(kill-line)
(kill-buffer)
(yank))`
${7:
Self Beliefs:
+ I am working on a healthier lifestyle, including a low-carb diet.
- I’m also investing in a healthy, long-lasting relationship.
➩ These are what I want and are important to me. ⇦
+ I will not use any substances to avoid real issues in my life. I must own them.
+ Everything I’m searching for is already inside of me.
+ Progress is more important than perfection.
⟨ Press TAB once you've read these beliefs. ⟩
$(when yas-moving-away-p "")
}
*Three things I'm grateful for:*
1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I
have today, something simple near me within sight}
2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I
have today, something simple near me within sight}
3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I
have today, something simple near me within sight}
*Three things that would make today great:*
1. ${11:???}
2. ${12:???}
3. ${13:???}
*What one thing is top of mind today?*
${14:???}
*What’s one opportunity I want to go after?*
${15:???}
*What’s one thing I’m really proud of OR I’m amazed and in awe of?*
${16:???}
$0
Besides a bit of webscraping to obtain a daily inspirational quote image, and the necessary yasnippet code, this template was taken from a discussion on Hacker news: “I find journaling indispensable”. In time, I will likely alter it to meet my needs, but I like it as it is right now (•̀ᴗ•́)و
Here are some of my common workflow states, —the ‘X/Y’ indicates to do action ‘X’ when entering a state and ‘Y’ when leaving it, with ‘!’ denoting a timestamp should be generated and ‘@’ denoting a user note should be made.
(setq org-todo-keywords
'((sequence "TODO(t)" "STARTED(s@/!)" "|" "DONE(d/!)")
(sequence "WAITING(w@/!)" "ON_HOLD(h@/!)" "|" "CANCELLED(c@/!)")))
;; Since DONE is a terminal state, it has no exit-action.
;; Let's explicitly indicate time should be noted.
(setq org-log-done 'time)
The @
brings up a pop-up to make a local note about why the state changed.
Super cool stuff!
In particular, we transition from TODO
to STARTED
once 15 minutes, or a
reasonable amount, of work has transpired. Since all but one state are marked
for logging, we could use the lognotestate
logging facility of org-mode, which
prompts for a note every time a task’s state is changed.
Entering a comment about what I’ve done, even if it’s very little, feels like
I’m getting something done. It’s an explicit marker of progress and motivates me
to want to change my task’s states more often until I see it marked DONE
.
Here’s how they are coloured,
(setq org-todo-keyword-faces
'(("TODO" :foreground "red" :weight bold)
("STARTED" :foreground "blue" :weight bold)
("DONE" :foreground "forest green" :weight bold)
("WAITING" :foreground "orange" :weight bold)
("ON_HOLD" :foreground "magenta" :weight bold)
("CANCELLED" :foreground "forest green" :weight bold)))
Now we press C-c C-t
then the letter shortcut to actually make the state of an org heading.
(setq org-use-fast-todo-selection t)
We can also change through states using Shift- left, or right.
Let’s draw a state diagram to show what such a workflow looks like.
PlantUML supports drawing diagrams in a tremendously simple format —it even supports Graphviz/DOT directly and many other formats. Super simple setup instructions can be found here; below are a bit more involved instructions. Read the manual here.
;; Install the tool
; (async-shell-command "brew cask install java") ;; Dependency
; (async-shell-command "brew install plantuml")
;; Tell emacs where it is.
;; E.g., (async-shell-command "find / -name plantuml.jar")
(setq org-plantuml-jar-path
(expand-file-name "/usr/local/Cellar/plantuml/1.2019.13/libexec/plantuml.jar"))
;; Enable C-c C-c to generate diagrams from plantuml src blocks.
(add-to-list 'org-babel-load-languages '(plantuml . t) )
(require 'ob-plantuml)
; Use fundamental mode when editing plantuml blocks with C-c '
(add-to-list 'org-src-lang-modes '("plantuml" . fundamental))
Let’s use this!
skinparam defaultTextAlignment center /' Text alignment '/
skinparam titleBorderRoundCorner 15
skinparam titleBorderThickness 2
skinparam titleBorderColor red
skinparam titleBackgroundColor Aqua-CadetBlue
title My Personal Task States
[*] -> Todo /' This is my starting state '/
Done -right-> [*] /' This is an end state '/
Cancelled -up-> [*] /' This is an end state '/
/'A task is “Todo”, then it's “started”, then finally it's “done”. '/
Todo -right-> Started
Started -down-> Waiting
Waiting -up-> Started
Started -right-> Done
/'Along the way, I may pause the task for some reason then
return to it. This may be since I'm “Blocked” since I need
something, or the task has been put on “hold” since it may not
be important right now, and it may be “cancelled” eventually.
'/
Todo -down-> Waiting
Waiting -up-> Todo
Waiting -up-> Done
Todo -down-> On_Hold
On_Hold -> Todo
On_Hold -down-> Cancelled
Waiting -down-> Cancelled
Todo -down-> Cancelled
/' The Org-mode shortcuts for these states are as follows. '/
Todo : t
On_Hold : h
Started : s
Waiting : w
Cancelled : c
Done : d
/' If a task is paused, we should document why this is the case. '/
note right of Waiting: Note what is\nblocking us.
note right of Cancelled: Note reason\nfor cancellation.
note bottom of On_Hold: Note reason\nfor reduced priority.
center footer ♥‿♥ Org-mode is so cool (•̀ᴗ•́)و
/' Note that we could omit the “center, left, right” if we wished,
or used a “header” instead.'/
Of note:
- Multiline comments are with
/' comment here '/
, single quote starts a one-line comment. - Nodes don’t need to be declared, and their names may contain spaces if they are enclosed in double-quotes.
- One forms an arrow between two nodes by writing a line with
x ->[label here] y
ory <- x
; or using-->
and<--
for dashed lines. The label is optional.To enforce a particular layout, use
-X->
whereX ∈ {up, down, right, left}
. - To declare that a node
x
has fieldsd, f
we make two new lines havingx : f
andx : d
. - One adds a note near a node
x
as follows:note right of x: words then newline\nthen more words
.Likewise for notes on the
left, top, bottom
.- A note can be on several lines. It’s terminated by
end note
.
- A note can be on several lines. It’s terminated by
- Interesting sprites and many other things can be done with PlantUML. Read the docs.
This particular workflow is inspired by Bernt Hansen —while quickly searching through the PlantUML manual: The above is known as an “activity diagram” and it’s covered in §4.
Org-mode may be used with PlantUML:
- See §11,12 for using Org-mode notation to form ‘mindmaps’ and ‘work breakdown structures’.
- Org-mode text formatters are also acknowledged but the delimiters must be doubled; see §16.1.
You can quickly write and see the resulting UMLs using https://liveuml.com/, for the most part.
Let’s keep track of the time we spend working on tasks that we may have captured for ourselves the previous day. Such statistics provides a good idea of how long it actually takes me to accomplish a certain task in the future and it lets me know where my time has gone.
- Clock in
- on a heading with
I
, or in the subtree withC-c C-x C-i
. - Clock out
- of a heading with
O
, or in the subtree withC-c C-x C-o
. - Clock report
- See clocked times with
C-c C-x C-r
.
After clocking out, the start and end times, as well as the elapsed time, are added to a drawer to the heading. We can punch in and out of tasks as many times as desired, say we took a break or switched to another task, and they will all be recorded into the drawer.
;; Record a note on what was accomplished when clocking out of an item.
(setq org-log-note-clock-out t)
To get started, we could estimate how long a task will take and clock-in; then clock-out and see how long it actually took.
Sometimes, at the beginning at least, I would accidentally invoke the transposed
command C-x C-c
, which saves all buffers and quits Emacs. So here’s a helpful
way to ensure I don’t quit Emacs accidentally.
(setq confirm-kill-emacs 'yes-or-no-p)
A few more settings:
;; Resume clocking task when emacs is restarted
(org-clock-persistence-insinuate)
;; Show lot of clocking history
(setq org-clock-history-length 23)
;; Resume clocking task on clock-in if the clock is open
(setq org-clock-in-resume t)
;; Sometimes I change tasks I'm clocking quickly ---this removes clocked tasks with 0:00 duration
(setq org-clock-out-remove-zero-time-clocks t)
;; Clock out when moving task to a done state
(setq org-clock-out-when-done t)
;; Save the running clock and all clock history when exiting Emacs, load it on startup
(setq org-clock-persist t)
;; Do not prompt to resume an active clock
(setq org-clock-persist-query-resume nil)
;; Include current clocking task in clock reports
(setq org-clock-report-include-clocking-task t)
Use one of the following options, with the top-most being the first to be tried.
- From anywhere,
C-u C-c C-x C-i
yields a pop-up for recently clocked in tasks. - Pick something off today’s agenda scheduled items.
- Pick a
Started
task from the agenda view, work on this unfinished task. - Pick something from the
TODO
tasks list in the agenda view.
C-c C-x C-d
also provides a quick summary of clocked time for the current org file.
Before clocking into a task, add to the properties drawer :Effort: 1:25
or C-c
C-x C-e
, for a task that you estimate will take an hour and twenty-five minutes,
for example. Now the modeline will mention the time elapsed alongside the task
name. Woah!
- This is also useful when you simply want to put a time limit on a task that
wont be completed anytime soon, say writing a thesis or a long article, but
you still want to work on it for an hour a day and be warned when you exceed
such a time constraint.
When you’ve gone above your estimate time, the modeline colours it red.
The reason to use habits is that they come with a graph indicating consistency by colour, and the goal of the game is to have the longest possible chain —no red days!
A ‘habit’ is a usual (recurring) todo task marked as a habit:
Use C-c C-x p
to set the STYLE
property to habit
on a task to set it as a habit.
;; Show habits for every day in the agenda.
(setq org-habit-show-habits t)
(setq org-habit-show-habits-only-for-today nil)
;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading.
(setq org-habit-graph-column 90)
;; In order to see the habit graphs, which I've placed rightwards, let's
;; always open org-agenda in ‘full screen’.
(setq org-agenda-window-setup 'only-window)
inch by inch anything’s a cinch! |
!
means today and ⋆
means a task has been done on that day;
intuitively green means you’re on track, yellow is warning sign of overdue,
red is overdue, and blue is an acceptable break day.
Here’s an example habit from the Org-mode manual, where .+𝒳d/𝒴d
reads
perform the habit once every 𝒳 days, but never let me go 𝒴 days without doing it.
** TODO Shave SCHEDULED: <2020-01-08 Wed .+2d/4d> :PROPERTIES: :STYLE: habit :END:
Shave every 2 days, but we can take a 3-day break; however, on the 4th day, gotta shave!
Remember that in the agenda view if you alter a task, say with t
to mark it
done, then you need to use s
to save the underlying todo/notes files; otherwise,
any g
will revert the change in the agenda buffer.
Herein we configure utilites for version control, function and variable lookup, and template expansion for inescapably repetitive scenarios.
Usually Emacs only highlights macro names, the following incantation makes it
highlight all defined names —as long as we’re in Lisp mode, whence in org-src
blocks we use C-c '
.
;; Emacs Lisp specific
(use-package highlight-defined
:hook (emacs-lisp-mode . highlight-defined-mode))
Super helpful in making my Emacs configuration: If a name is not highlighted, then I’ve misspelled it or it doesn’t exist! 😄
In emacs-lisp-mode
we can enable eldoc-mode
to display information
about a function or a variable in the echo area. Likewise for Haskell.
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
(add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode)
(add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
(add-hook 'haskell-mode-hook 'turn-on-haskell-indent)
The less casual Haskeller would likely want to use intero to obtain more support; e.g., obtain suggestions from GHC about redundant imports or type signatures.
The more
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.
With a single space or tab, my code should always remain indented.
;; 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)
Let’s have, in a fringe, an indicator for altered regions in a version controlled file. The symbols “+, =” appear in a fringe by default for alterations —we may change these if we like.
;; Hunk navigation and commiting.
(use-package git-gutter
:diminish
:config (global-git-gutter-mode))
;; Diff updates happen in real time according when user is idle.
Let’s set a hydra so we can press C-x v n n p n
to move the next two
altered hunks, move back one, then move to the next. This saves me having
to supply the prefix C-x v
each time I navigate among my alterations.
At any point we may also press u 𝕩
to denote C-u ⟪prefix⟫ 𝕩
.
(defhydra hydra-version-control (global-map "C-x v")
"Version control"
;; Syntax: (extension method description)
("n" git-gutter:next-hunk "Next hunk")
("p" git-gutter:previous-hunk "Previous hunk")
("d" git-gutter:popup-hunk "Show hunk diff")
("r" git-gutter:revert-hunk "Revert hunk\n")
("c" git-gutter:stage-hunk "Stage hunk")
("s" git-gutter:statistic "How many added & deleted lines"))
Commiting with C-x v c
let’s us use C-c C-k
to cancel and C-c C-c
to
submit the given message; C-c C-a
to amend the previous commit.
Alternatively, we may use diff-hl:
;; Colour fringe to indicate alterations.
;; (use-package diff-hl)
;; (global-diff-hl-mode)
A few more helpful version control features:
;; Popup for who's to blame for alterations.
(use-package git-messenger
:config ;; Always show who authored the commit and when.
(setq git-messenger:show-detail t)
;; Message menu let's us use magit diff to see the commit change.
(setq 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.
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.
Basic support todos. By default these include: TODO NEXT THEM PROG OKAY DONT FAIL DONE NOTE KLUDGE HACK TEMP FIXME and any sequence of X’s or ?’s of length at least 3: XXX, XXXX, XXXXX, …, ???, ????, ????, ….
;; 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
(global-hl-todo-mode) ;; Enable it everywhere.
;; Adding new keywords
(loop for kw in '("TEST" "MA" "WK" "JC")
do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))))
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
:config
;; For some reason cannot use :custom with this package.
(custom-set-variables
'(magit-todos-keywords (list "TODO" "FIXME" "MA" "WK" "JC")))
;; Ignore TODOs mentioned in exported HTML files; they're duplicated from org src.
(setq magit-todos-exclude-globs '("*.html"))
(magit-todos-mode))
Note that such TODO keywords are not propagated from sections that are COMMENT-ed out in org-mode.
Open a Magit status buffer, or run magit-todos-list
to show a dedicated to-do
list buffer. You can then peek at items with space, or jump to them with enter.
Seeing the TODO list with each commit is an incentive to actually tackle the items there (•̀ᴗ•́)و
- Ensure you exclude generated files, such as the Emacs backups directory,
from being consulte. Using
magit
, pressi
to mark items to be ignored. - This feature also works outside of git repos.
Add these to the version control hydra.
(defhydra hydra-version-control (global-map "C-x v")
("t" helm-magit-todos "Show TODOs lists for this repo."))
Flycheck is a on-the-fly syntax checker that relies on external programs to check buffers; which must be installed separately.
- E.g., ghc is required for Haskell; whereas Emacs Lisp is checked by Emacs’
own byte compiler,
emacs-lisp
. - Sometimes more than one checking tool applies, use
C-c ! s
to select a different checker. C-c ! n,p,l
takes you to the ‘n’ext or ‘p’revious error, or ‘l’ist all errors in another buffer.C-c ! c
to explicitly recheck the buffer.
(use-package flycheck
:diminish
:init (global-flycheck-mode)
:config ;; There may be multiple tools; I have GHC not Stack, so let's avoid that.
(setq-default flycheck-disabled-checkers '(haskell-stack-ghc emacs-lisp-checkdoc))
:custom (flycheck-display-errors-delay .3))
In an org-src block, we press C-c '
to get into the language’s mode where
flycheck will provide warnings.
module Main where
main :: IO ()
main = putStrLn $ "nice" ++ f 0
f :: Int -> String
f x = x -- show x
-- type error
In-general, flycheck is intended for self-contained raw code —not for source
blocks in Org-mode. Whence, the above example is a complete Haskell program,
with a named module and main
method.
I think the built-in flymake syntax checker is better for Emacs Lisp, so let’s use that for ELisp.
(use-package flymake
:hook ((emacs-lisp-mode . (lambda () (flycheck-mode -1)))
(emacs-lisp-mode . flymake-mode))
:bind (:map flymake-mode-map
("C-c ! n" . flymake-goto-next-error)
("C-c ! p" . flymake-goto-prev-error)))
Try it out:
(setq 1 2) ;; Error: ‘1’ is not a variable.
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!
- Generally this only includes a language’s keywords, such as
- 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.
- This makes it much easier to visually spot dependencies with a quick glance.
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))
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:
- Coding in color: How to make syntax highlighting more useful —an excellent, terse, read
- C++ IDE Evolution: From Syntax Highlighting to Semantic Highlighting
- Names with a similar prefix share a colour, and class-local items share a colour.
- Lexical differential highlighting instead of syntax highlighting
- Ideally, the smaller the lexical difference, the greater the color difference should be.
- Colouring by Context —an Emacs package
- A case against syntax highlighting
Literate programming within Org-mode is not always ideal, 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.
We use a feature-full folding mode, Origami-mode.
(use-package origami)
With basic support for one of my languages.
(push (cons 'agda2-mode (origami-markers-parser "{-" "-}"))
origami-parser-alist)
With expected support for searching.
(defun my/search-hook-function ()
(when origami-mode (origami-toggle-node (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)
Along with a hydra for super quick navigation and easily folding, unfolding blocks! Love this one ^_^
(defhydra folding-with-origami-mode (global-map "C-c f")
("h" origami-close-node-recursively "Hide")
("o" origami-open-node-recursively "Open")
("t" origami-toggle-all-nodes "Toggle buffer")
("n" origami-next-fold "Next")
("p" origami-previous-fold "Previous"))
We can use C-x o
to switch to the ‘o’ther window, and C-u 𝓃 C-x o
to switch to
the 𝓃-th next clockwise window, but using s-↑,↓,←,→
may be faster.
(use-package windmove
:config ;; use command key on Mac
(windmove-default-keybindings 'super)
;; wrap around at edges
(setq windmove-wrap-around t))
The docs, for the following, have usage examples.
(use-package buffer-flip
:bind
(:map buffer-flip-map
("M-<tab>" . buffer-flip-forward)
("M-S-<tab>" . buffer-flip-backward)
("C-g" . buffer-flip-abort))
:config
(setq buffer-flip-skip-patterns
'("^\\*helm\\b")))
;; key to begin cycling buffers.
(global-set-key (kbd "M-<tab>") 'buffer-flip)
See buffer-move if you’re interested in moving the buffers, and their windows, into new configurations.
It is common that there is a sequence of text that we tend to repeat often, possibly with a name or some other parameter altered. Such a ‘snippet’ could be written once then provided by a simple Lisp insert command with the parameters being queried. Luckily, others have written such pleasant utilities.
Yasnippet is a pleasant utility for template expansion with the alluring
feature to allow arbitrary Lisp code to be executed during expansion.
The declaration of templates is verbose, requiring a particular file
hierarchy, as such I utilise Yankpad which allows me to employ
an Org-mode approach: Each template corresponds to an org heading of
the form Key:Words:For:Expansion:Here: name of snippet here
and the
template body is then the body of the org heading.
Any of Key, Words, For, Expansion, Here
will rewrite into the body
of the org tree. This is much more terse, and I even don’t bother
with that; instead preferring to tangle my templates using yankpad
as a mere interface. It is important to note that Yankpad also provides
features that are not in Yassnippet, such as allowing arbitrary language
code to be executed —one simply uses an org-src block!
There are only be one major completion backend for any mode, but
other backends can serve as secondary ones. Here’s a function to
make company-yankpad
a secondary of all existing backends.
;; Add yasnippet support for all company backends
;;
(cl-defun my/company-backend-with-yankpad (backend)
"There can only be one main completition backend, so let's
enable yasnippet/yankpad as a secondary for all completion
backends.
Src: https://emacs.stackexchange.com/a/10520/10352"
(if (and (listp backend) (member 'company-yankpad backend))
backend
(append (if (consp backend) backend (list backend))
'(:with company-yankpad))))
;; Yet another snippet extension program
(use-package yasnippet
:diminish yas-minor-mode
:config
(yas-global-mode 1) ;; Always have this on for when using yasnippet syntax within yankpad
;; respect the spacing in my snippet declarations
(setq yas-indent-line 'fixed))
;; Alternative, Org-based extension program
(use-package yankpad
:diminish
:config
;; Location of templates
(setq yankpad-file "~/.emacs.d/yankpad.org")
;; Ignore major mode, always use defaults.
;; Yankpad will freeze if no org heading has the name of the given category.
(setq yankpad-category "Default")
;; Load the snippet templates ---useful after yankpad is altered
(yankpad-reload)
;; Set company-backend as a secondary completion backend to all existing backends.
(setq company-backends (mapcar #'my/company-backend-with-yankpad company-backends)))
With these settings, along with the company
backend, I may type a keyword then
“tab” it into expansion.
Yankpad requires we have an org file that contains our templates, so we tangle such a file ~~/.emacs.d/yankpad.org~, and have all of our templates be globally accessible.
#+Description: This is file is generated from my init.org; do not edit.
* Default :global:
Here’s an example of a common template I perform by hand —no more!
I have the expected habit of copying a URL from someplace then forming
a link to it by writing [[URL] [description]]
, since the URL & syntax are already
known, let’s expand those and place the cursour at the only unknown —the description.
** my_org_insert_link: cleverly insert a link copied to clipboard
[[${1:`(clipboard-yank)`}][$2]] $0
What’s going on here?
- This template is expanded with the keyword
my-org-insert-link
, then “tab”. - The cursour lands at position
$1
, which has default text being the result of evaluating(clipboard-yank)
.We may evaluate Lisp code anywhere by enclosing it in backticks.
- If we’re satisfied with the current field, we simply tab to the next field. Otherwise, we simply write text –which overwrites the default text.
- After enough tabbing we complete the template and the cursour lands
at position
$0
.
⟪ Having default or mirrored text for $2
would not allow me to see the URL
field, lest I wish to change it or at least confirm it’s what I want.
Hence, the $2
field has no default. ⟫
Let’s overwrite the usual way to insert such links, via C-c C-l
.
(cl-defun org-insert-link ()
"Makes an org link by inserting the URL copied to clipboard and
prompting for the link description only.
Type over the shown link to change it, or tab to move to the
description field.
This overrides Org-mode's built-in ‘org-insert-link’ utility;
whence C-c C-l uses the snippet."
(interactive)
(insert "my_org_insert_link")
(yankpad-expand))
Warning! Snippet names cannot have hypens in them —in this setup at least.
The Yasnippet manual is an accessible read, as is the Yankpad manual, and
showcases many other utilities; such as having certain snippets being
enabled only in particular modes or on demand. Of note is that field $n
can be
accessed in code with the invocation (yas-field-value n)
.
Incidentally, I used this snippet setup to demo the idea of repetitious code in grouping constructs within dependently-typed languages, which was accepted and led to my doctoral research on a ‘do it yourself module system’.
The rest of this section is other templates, not much for now, concluding with actually loading this snippet mechanism globally.
The remaining subsections discuss contents of my yankpad file.
This produces a pop-up list of org-mode block types, if src
is selected, then a
list of my commonly used languages pops-up. Alternatively, ignore the pop-up
menu and write any block or language name.
** begin: produce an org-mode block
#+begin_${1:environment$(let*
((block '("src" "example" "quote" "verse" "center" "latex" "html" "ascii"))
(langs '("c" "emacs-lisp" "lisp" "latex" "python" "sh" "haskell" "plantuml" "prolog"))
(type (yas-choose-value block)))
(concat type (when (equal type "src") (concat " " (yas-choose-value langs)))))}
$0
#+end_${1:$(car (split-string yas-text))}
In this case, yas-text
is equivalent to (yas-field-value 1)
;
it generally refers to the value of the field being mirrored with ${n: ⋯yas-text⋯}
.
However, going through pop-ups takes precious time —besides being slightly annyoing. Let’s introduce a template for my most utilised kind of language blocks.
** s_org: src block for org #+begin_src org $0 #+end_src
However, doing this for each language I want is a waste of time and textual
space. Why? The purpose of templates is to reduce repetition, yet the above
block would be repeated with only 3 parts ‘unknown’: The expansion keyword, the
description, and the org-mode source block name. Whence, the template text is
generated by the following basic loop —whose source block is named
my-org-lang-templates
.
;; We make an org BLOCK snippet template for each LANG the user has declared.
;;
(loop for (shortcut . block) in '(("s_" . "src")
("e_" . "example")
("q_" . "quote")
("v_" . "verse")
("c_" . "center")
("ex_" . "export"))
concat (loop for lang in (-cons* "org" "agda2" "any" ;; Extra ‘languages’
;; Also include whatever languages we've loaded for literate programming.
(--map (symbol-name (car it)) org-babel-load-languages))
for key = (concat shortcut lang)
for description = (concat block " block for " lang)
concat (concat "\n** " key ": " description
"\n#+begin_" block " " lang
"\n$0"
"\n#+end_" block "\n")))
The resulting text of this block, generated below, is tangled to our yankpad by utilising a noweb source block invocation. An example of the resulting text is the above “s_org” block. The result is 83 template expansions —that would have been a bit much to write by hand.
#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes <<my-org-lang-templates()>> #+end_src
Now s_
, due to company mode, brings up a list of languages that I can then
scroll down through, then “enter” upon to expand. Moreover, the prefix s_
means
that the key is mostly irrelevant, since I needn’t remember it because
company-mode immediately lists possible completions along with the descriptions
for the snippets. Likewise for examples with e_
or quotes with q_
. Super neat
stuff :-)
Ain’t this reminiscent of meta-programming ;-)
Using noweb
invocations, any time the tangling is performed, the yankpad
is kept up to date –no personal intervention from myself.
The following snippets were rather useful as I began learning Lisp to construct
my editor of choice —I love Emacs so much. Admittedly, I still need the first
one below and usually beat around the bush by using (loop for ⋯ do ⋯)
, which is
‘noisier’ but easier to remember and to read for non-Lispers.
** loop: Elisp's for each loop
(dolist (${1:var} ${2:list-form})
${3:body})
** defun: Lisp functions
(cl-defun ${1:fun-name} (${2:arguments})
"${3:documentation}"
$0
)
** cond: Elisp conditionals
(cond (${1:scenario₁} ${2:response₁})
(${3:scenario₂} ${4:response₂})
)
To show ℒ = ℛ
, one starts at the complicated side, say ℒ, then, with the aim of
simplification, tries to end at the simpler side, 𝓡. Along the way, one
justifies each step of the calculation. This approach is popular in the proof
assistant Agda; Examples. Read more about informal calculational proofs.
** fun: Function declaration with type signature
${1:fun-name} : ${2:arguments}
$1 ${3:args} = ?$0
** eqn_begin: Start a ≡-Reasoning block in Agda
begin
${1:complicated-side}
$0≡⟨ ${3:reason-for-the-equality} ⟩
${2:simpler-side}
∎
** eqn_step: Insert a step in a ≡-Reasoning block in Agda
≡⟨ ${2:reason-for-the-equality} ⟩
${1:new-expression}
$0
One expands eqn_begin
, tabs to fill in the three main locations, then
immediately types eqn_step
to produce a new step in a calculational proof.
In this setup, I have some templates appear elsewhere, tagged with :noweb-ref
templates-from-other-places-in-my-init
. They are presented in natural positions,
but can only occur to the machine after template expansion is setup. Using
org-mode, we are able to present code in any order and tangle it to the order
the compilers need it to be!
Let’s activate all such templates, now after template expansion has been setup.
<<templates-from-other-places-in-my-init>>
After our yankpad templates are generated, we need to load it.
;; After init hook; see above near use-package install.
(yankpad-reload)
Here is a collection of Emacs-lisp functions that I have come to use in other files.
Disclaimer: I wrote much of the following before I learned any lisp; everything below is probably terrible.
Let’s save a few precious seconds,
;; change all prompts to y or n
(fset 'yes-or-no-p 'y-or-n-p)
;; Enable all ‘possibly confusing commands’ such as helpful but
;; initially-worrisome “narrow-to-region”, C-x n n.
(setq-default disabled-command-function nil)
Let documentation pop-up when we pause on a completion.
This is very useful when editing in a particular coding language, say via
C-c '
for org-src blocks.
(use-package company-quickhelp
:config
(setq company-quickhelp-delay 0.1)
(company-quickhelp-mode))
⟨ I was a bit too Emacs-happy at one-point; this’ cool, but I rarely use it;
except C-x b
: A buffer approach is far superior to a tab-based one. ⟩
I’ve downloaded the Vimium extension for Google Chrome,
and have copy-pasted these Emacs key bindings into it.
Now C-h
in my browser shows which Emacs-like bindings
can be used to navigate my browser ^_^
⟨ I was a bit too Emacs-happy at one-point; this’ cool, but I rarely use it. ⟩
Using the Emacs-Anywhere tool, I can press Cmd Shift e
to have an Emacs frame
appear, produce text with Emacs editing capabilities, then C-x 5 0
to have the
resulting text dumped into the text area I was working in.
This way I can use Emacs literally anywhere for textual input!
For my Mac OSX:
(shell-command "curl -fsSL https://raw.github.com/zachcurry/emacs-anywhere/master/install | bash")
(server-start)
The tools that use emacs-anywhere —such as my web browser— and emacs-anywhere itself need to be given sufficient OS permissions:
System Preferences → Security & Privacy → Accessibility
Then check the emacs-anywhere box from the following gui and provide a keyboard shortcut:
System Preferences → Keyboard → Shortcuts → Services
(•̀ᴗ•́)و
I always want to be in Org-mode and input unicode:
(add-hook 'ea-popup-hook
(lambda (app-name window-title x y w h)
(org-mode)
(set-input-method "Agda")))
I do this so often it’s not even funny.
(global-set-key [f5] '(lambda () (interactive) (revert-buffer nil t nil)))
In Mac OS, one uses Cmd-r
to reload a page and Emacs binds buffer reversion to Cmd-u
–in Emacs, Mac’s Cmd
is referred to as the ‘super key’ and denoted s
.
Moreover, since I use Org-mode to generate code blocks and occasionally inspect them, it would be nice if they automatically reverted when they were regenerated –Emacs should also prompt me if I make any changes!
;; Auto update buffers that change on disk.
;; Will be prompted if there are changes that could be lost.
(global-auto-revert-mode 1)
;; Don't show me the “ARev” marker in the mode line
(diminish 'auto-revert-mode)
Dual to C-k
,
;; M-k kills to the left
(global-set-key "\M-k" '(lambda () (interactive) (kill-line 0)) )
Let’s extend the standard C-x k
with prefix support, so that we can invoke
variations: Kill this buffer, kill other buffer, or kill all other buffers.
By default C-x k
prompts to select which buffer should be selected. I almost
always want to kill the current buffer, so let’s not waste time making such a
tedious decision. Moreover, if I’ve killed a buffer, I usually also don’t want
the residual window, so let’s get rid of it.
(global-set-key (kbd "C-x k")
(lambda (&optional prefix)
"C-x k ⇒ Kill current buffer & window
C-u C-x k ⇒ Kill OTHER window and its buffer
C-u C-u C-x C-k ⇒ Kill all other buffers and windows
Prompt only if there are unsaved changes."
(interactive "P")
(pcase (or (car prefix) 0)
;; C-x k ⇒ Kill current buffer & window
(0 (kill-this-buffer)
(unless (one-window-p) (delete-window)))
;; C-u C-x k ⇒ Kill OTHER window and its buffer
(4 (other-window 1)
(kill-this-buffer)
(unless (one-window-p) (delete-window)))
;; C-u C-u C-x C-k ⇒ Kill all other buffers and windows
(16 (mapc 'kill-buffer (delq (current-buffer) (buffer-list)))
(delete-other-windows)))))
The incantation C-u C-x k
will reduce the noise of all the documentation buffers
I tend to consult.
I often find myself switching from a horizontal view of two windows in Emacs to a
vertical view. This requires a variation of C-x 1 RET C-x 3 RET C-x o C-x b RET
.
Instead I now only need to type C-|
to make this switch.
(defun ensure-two-vertical-windows ()
"I used this method often when programming in Coq.
When there are two vertical windows, this method ensures the left-most
window contains the buffer with the cursour in it."
(interactive)
(let ((otherBuffer (buffer-name)))
(other-window 1) ;; C-x 0
(delete-window) ;; C-x 0
(split-window-right) ;; C-x 3
(other-window 1) ;; C-x 0
(switch-to-buffer otherBuffer) ;; C-x b RET
(other-window 1)))
(global-set-key (kbd "C-|") 'ensure-two-vertical-windows)
Org-mode settings are, for the most part, in the form #+KEYWORD: VALUE
. Of notable interest
are the TITLE
and NAME
keywords. We use the following org-keywords
function to obtain
the values of arbitrary #+THIS : THAT
pairs, which may not necessarily be supported by native
Org-mode –we do so for the case, for example, of the CATEGORIES
and IMAGE
tags associated with an article.
;; Src: http://kitchingroup.cheme.cmu.edu/blog/2013/05/05/Getting-keyword-options-in-org-files/
(defun org-keywords ()
"Parse the buffer and return a cons list of (property . value) from lines like: #+PROPERTY: value"
(org-element-map (org-element-parse-buffer 'element) 'keyword
(lambda (keyword) (cons (org-element-property :key keyword)
(org-element-property :value keyword)))))
(defun org-keyword (KEYWORD)
"Get the value of a KEYWORD in the form of #+KEYWORD: value"
(cdr (assoc KEYWORD (org-keywords))))
Note that capitalisation in a ”#+KeyWord” is irrelevant.
See here on how to see the abstract syntax tree of an org file and how to manipulate it.
I try to blog occasionally, so here’s a helpful function to quickly publish the current article to my blog.
(define-key global-map "\C-cb" 'my/publish-to-blog)
(cl-defun my/publish-to-blog (&optional (draft nil) (local nil))
"
Using ‘AlBasmala’ setup to publish current article to my blog.
Details of AlBasmala can be found here:
https://alhassy.github.io/AlBasmala/
Locally: ~/alhassy.github.io/content/AlBasmala.org
A ‘draft’ will be produced in about ~7 seconds, but does not re-produce
a PDF and the article has a draft marker near the top. Otherwise,
it will generally take ~30 seconds due to PDF production, which is normal.
The default is not a draft and it takes ~20 seconds for the live
github.io page to update.
The ‘local’ optiona indicates whether the resulting article should be
viewed using the local server or the live webpage. Live page is default.
When ‘draft’ and ‘local’ are both set, the resulting page may momentarily
show a page-not-found error, simply refresh.
"
(load-file "~/alhassy.github.io/content/AlBasmala.el")
;; --MOVE ME TO ALBASMALA--
;; Sometimes the file I'm working with is not a .org file, so:
(setq file.org (buffer-name))
(preview-article :draft draft)
(unless draft (publish))
(let ((server (if local "http://localhost:4000/" "https://alhassy.github.io/")))
(async-shell-command (concat "open " server NAME "/") "*blog-post-in-browser*"))
)
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.
- 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’.
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.
- Use
- 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!