GitHub - alhassy/emacs.d: My Emacs configuration, literately
source link: https://github.com/alhassy/emacs.d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
A Life Configuring Emacs
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?C-h ?
⇒ What’re the help topics? —gives possible completions to “C-h ⋯”.- “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.
Table of Contents
Why Emacs?
Emacs is a flexible platform for developing end-user applications —unfortunately it is generally perceived as merely a text editor. Some people use it specifically for one or two applications.
For example, writers use it as an interface for Org-mode and others use it as an interface for version control with Magit. Org is an organisation tool that can be used for typesetting which subsumes LaTeX, generating many different formats –html, latex, pdf, etc– from a single source, keeping track of schedules & task management, blogging, habit tracking, personal information management tool, and much more. Moreover, its syntax is so natural that most people use it without even knowing! For me, Org allows me to do literate programming: I can program and document at the same time, with no need to seperate the two tasks and with the ability to generate multiple formats and files from a single file.
If you are a professional writer…Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish. —Neal Stephenson, In the beginning was the command line
- Extensible ⇒ IDEs are generally optimised for one framework, unlike Emacs!
- You can program Emacs to automate anything you want.
- Hence, it’s an environment, not just an editor.
- ⇒ Unified keybinding across all tools in your environment.
Users are given a high-level full-featured programming language, not just a small configuration language. For the non-programmers, there is Custom, a friendly point-and-click customisation interface.
- Self Documented ⇒ Simply
M-x info-apropos
orC-h d
to search all manuals or look up any function provided by Emacs! - Mature ⇒ tool with over 40 years of user created features
- Plugins for nearly everything!
- No distinction between built-ins and user-defined features! (Lisp!)
- You can alter others’ code without even touching the source.
- Advising functions and ‘hooking’ functionality onto events.
- Free software ⇒ It will never die!
- Emacs is one of the oldest open source projects still under developement.
Of course Emacs comes with the basic features of a text editor, but it is much more; for example, it comes with a powerful notion of ‘undo’: Basic text editors have a single stream of undo, yet in Emacs, we have a tree –when we undo and make new edits, we branch off in our editing stream as if our text was being version controlled as we type! –We can even switch between such branches!
;; Allow tree-semantics for undo operations. (use-package undo-tree :diminish ;; Don't show an icon in the modeline :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
Emacs is a flexible platform for developing end-user applications
Just as a web browser is utilised as a platform for deploying applications, or ‘extensions’, written in JavaScript that act on HTML documents, Emacs is a platform for deploying applications written in Emacs Lisp that act on buffers of text. In the same vein, people who say Emacs having Tetris is bloat are akin to non-coders who think their browser has bloat since it has a “view page source” feature —which nearly all browsers have, yet it’s only useful to web developers. Unlike a web browser in which the user must get accustomed to its features, Emacs is customised to meet the needs of its user. ( Incidentally, Emacs comes bundled with a web browser. )
In the case of Emacs the boundary between user and programmer is blurred as adapting the environment to one’s needs is already an act of programming with a very low barrier to entry. —rekado
- Don’t just get used to your tool, make it get used to you!
Emacs is not just an editor, but a host for running Lisp applications!
For example, Emacs is shipped as a language-specific IDE to a number of communities —e.g., Oz, Common Lisp, and, most notably, Agda. Emacs is a great IDE for a language —one just needs to provide a ‘major mode’ and will then have syntax highlighting, code compleition, jumping to definitions, etc. There is no need to make an IDE from scratch.
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. )
⇒ MacOS supports many Emacs shortcuts, system-wide, such as C-a/e, C-d, C-k/y,
C-o, C-p/n
and even C-t
for transposing two characters. ⇐
Emacs Proverbs as Koan
Below is an extract from William Cobb’s “Reflections on the Game of Go”, with minor personalised adjustements for Emacs. Enjoy!
The Japanese term satori refers to the experience of enlightenment, the realisation of how things really are that is the primary aim of practice and meditation. However, the Zen tradition is famous for claiming that one cannot say what it is that one realises, that is, one cannot articulate the content of the enlightenment experience. Although it makes everything clear, it is an experience beyond words. Instead of being given an explanation of how things are, the student of Zen hears sayings called koan, often somewhat paradoxical in character, that come from those who are enlightened:
- “There are no CTRL and META.”
- “If you meet an Emacs you dislike, kill it.”
- “No one knows Emacs.”
- “One can only learn Emacs by living within it.”
- “To know Org mode is to know oneself.”
It is important to realise that koan are intended to move you off of one path and onto another. They are not just attempts to mystify you. For example, the first proverb is in regards to newcomers complaining about too many keybinings —eventually it’s muscle memory—, whereas the second is about using the right tool for the right task —Emacs is not for everyone. The fourth is, well, Emacs is an operating system.
Possibly Interesting Reads
—If eye-candy, a sleek and beautiful GUI, would entice you then consider starting with spacemacs. Here’s a helpful installation video, after which you may want to watch Org-mode in Spacemacs tutorial—
Remember: Emacs is a flexible platform for developing end-user applications; e.g., this configuration file is at its core an Emacs Lisp program that yields the editor of my dreams –it encourages me to grow and to be creative, and I hope the same for all who use it; moreover, it reflects my personality such as what I value and what I neglect in my workflow.
I’m stunned that you, as a professional software engineer, would eschew inferior computer languages that hinder your ability to craft code, but you put up with editors that bind your fingers to someone else’s accepted practice. —Howard Abrams
Fun commands to try out
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”
A neat way to get started with Emacs is to solve a problem you have, such as taking notes or maintaining an agenda —both with Org-mode.
Before we get started…
What Does Literate Programming Look Like?
Briefly put, literate programming in Emacs allows us to evaluate source code within our text files, then using the results as values in other source blocks. When presenting an algorithm, we can talk it out, with a full commentary thereby providing ‘reproducible research’: Explorations and resulting algorithms are presented together in a natural style.
⟨ This image was created in org-mode; details below or by looking at the source file ⟩
Here’s an example of showing code in a natural style, but having the resulting code appear in a style amicable to a machine. Here’s what you type:
It's natural to decompose large problems, #+begin_src haskell :noweb-ref defn-of-f :tangle no f = h ∘ g #+end_src But we need to define $g$ and $h$ /beforehand/ before we can use them. Yet it's natural to “motivate” their definitions ---rather than pull a rabbit out of hat. Org-let's us do that! Here's one, #+begin_src haskell :noweb-ref code-from-other-places :tangle no g = ⋯ #+end_src then the other. #+begin_src haskell :noweb-ref code-from-other-places :tangle no h = ⋯ #+end_src Of-course, we might also want a preamble: #+BEGIN_SRC haskell :tangle myprogram.hs import ⋯ #+END_SRC We can now tangle together the tagged code blocks in the order we want. #+BEGIN_SRC haskell :tangle myprogram.hs :comments none :noweb yes <<code-from-other-places>> <<defn-of-f>> #+END_SRC
Now C-c C-v C-t
yields a file named myprogram.hs
with contents in an order
amicable to a machine.
import ⋯ g = ⋯ h = ⋯ f = h ∘ g
Interestingly, unlike certain languages, Haskell doesn’t care too much about declaration order.
Warning! If we have different language blocks tangled to the same file, then
they are tangled alphabetically —e.g., if one of the blocks is marked
emacs-lisp
then its contents will be the very first one in the resulting source
file, since emacs-lisp
begins with e
which is alphabetically before h
of
haskell
.
Why a monolithic configuration?
Why am I keeping my entire configuration —from those involving cosmetics &
prose to those of agendas & programming— in one file? Being monolithic —“a
large, mountain-sized, indivisible block of stone”— is generally not ideal in
nearly any project: E.g., a book is split into chapters and a piece of software
is partitioned into modules. Using Org-mode, we can still partition our setup
while remaining in one file. An Emacs configuration is a personal, leisurely
project, and one file is a simple architecture: I don’t have to worry about many
files and the troubles of moving content between them; instead, I have headings
and move content almost instantaneously —org-refile by pressing w
at the start
of the reader. Moreover, being one file, it is easy to distribute and to extract
artefacts from it —such as the README for Github, the HTML for my blog, the
colourful PDF rendition, and the all-important Emacs Lisp raw code
file. Moreover, with a single #
I can quickly comment out whole sections,
thereby momentarily disabling features.
There’s no point in being modular if there’s nothing explaining what’s going on, so I document.
The concluding section of this read further argues the benefits of maintaining
literate, and monolithic, configuration files. As a convention, I will try to
motivate the features I set up and I will prefix my local functions with, well,
my/
—this way it’s easy to see all my defined functions, and this way I cannot
accidentally shadow existing utilities. Moreover, besides browsing the web, I do
nearly everything in Emacs and so the start-up time is unimportant to me: Once
begun, I have no intention of spawning another instance nor closing the current
one. ( Upon an initial startup using this configuration, it takes a total of
121 seconds to install all the packages featured here. )
Enjoy!
Booting Up
Let’s decide on where we want to setup our declarations for personalising Emacs to our needs. Then, let’s bootstrap Emacs’ primitive packaging mechanism with a slick interface —which not only installs Emacs packages but also programs at the operating system level, all from inside Emacs! Finally, let’s declare who we are and use that to setup Emacs email service.
~/.emacs
vs. init.org
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 format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using org-mode —/Emacs’ killer app/— which is discussed in great detail later on.
Adventure time! “Honey, where’s my init?”
Let’s use the three possible locations for the initialisation files to explore how Emacs finds them. Make the following three files.
~/.emacs.el
;; Emacs looks for this first; (set-background-color "chocolate3") (message-box ".emacs.el says hello")
~/.emacs
;; else; looks for this one; (set-background-color "plum4") (message-box ".emacs says hello")
~/.emacs.d/init.el
;; Finally, if neither are found; it looks for this one. (set-background-color "salmon") (message-box ".emacs.d/init.el says hello")
Now restart your Emacs to see how there super tiny initilaisation files affect your editor. Delete some of these files in-order for others to take effect!
Adventure time! Using Emacs’ Easy Customisation Interface
We have chosen not to keep configurations in ~~/.emacs~ since Emacs may explicitly add, or alter, code in it.
Let’s see this in action!
Execute the following to see additions to the ~~/.emacs~ have been added by ‘custom’.
M-x customize-variable RET line-number-mode RET
- Then press:
toggle
,state
, then1
. - Now take a look:
C-x C-f ~/.emacs
Support for ‘Custom’
Let the Emacs customisation GUI insert configurations into its own file, not
touching or altering my initialisation file. For example, I tend to have local
variables to produce README.org
’s and other matters, so Emacs’ Custom utility
will remember to not prompt me each time for the safety of such local variables.
(setq custom-file "~/.emacs.d/custom.el") (ignore-errors (load custom-file)) ;; It may not yet exist.
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)
use-package
—The start of init.el
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/"))) ;; 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)) (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 :defer 10 :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 :defer 5 :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 :defer 5 :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 :defer t) ;; 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 :defer t) ;; Lisp libraries with Haskell-like naming. (use-package dash) ;; “A modern list library for Emacs” (use-package s ) ;; “The long lost Emacs string manipulation library”. ;; 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>>
Finally, let’s try our best to have a useful & consistent commit log:
(defun my/git-commit-reminder () (insert "\n\n# The commit subject line ought to finish the phrase: # “If applied, this commit will ⟪your subject line here⟫.” ") (beginning-of-buffer)) (add-hook 'git-commit-setup-hook 'my/git-commit-reminder)
Super neat stuff!
README
—From init.org
to init.el
Rather than manually extracting the Lisp code from this literate document each
time we alter it, let’s instead add a ‘hook’ —a method that is invoked on a
particular event, in this case when we save the file. More precisely, in this
case, C-x C-s
is a normal save whereas C-u C-x C-s
is a save after forming
init.elc
and README.md
.
The my/make-init-el-and-README
function
We ‘hook on’ the following function to the usual save method that is associated with this file only.
(defun my/make-init-el-and-README () "Tangle an el and a github README from my init.org." (interactive "P") ;; Places value of universal argument into: current-prefix-arg (when current-prefix-arg (let* ((time (current-time)) (_date (format-time-string "_%Y-%m-%d")) (.emacs "~/.emacs") (.emacs.el "~/.emacs.el")) ;; Make README.org (save-excursion (org-babel-goto-named-src-block "make-readme") ;; See next subsubsection. (org-babel-execute-src-block)) ;; remove any other initialisation file candidates (ignore-errors (f-move .emacs (concat .emacs _date)) (f-move .emacs.el (concat .emacs.el _date))) ;; Make init.el (org-babel-tangle) ;; (byte-compile-file "~/.emacs.d/init.el") (load-file "~/.emacs.d/init.el") ;; Acknowledgement (message "Tangled, compiled, and loaded init.el; and made README.md … %.06f seconds" (float-time (time-since time)))))) (add-hook 'after-save-hook 'my/make-init-el-and-README nil 'local-to-this-file-please)
The Org-block named make-readme
Where the following block has #+NAME: make-readme
before it. This source block
generates the README
for the associated Github repository.
(save-buffer) (with-temp-buffer (insert "#+EXPORT_FILE_NAME: README.org # Logos and birthday present painting #+HTML:" (s-collapse-whitespace (concat " <p align=\"center\"> <img src=\"images/emacs-logo.png\" width=150 height=150/> </p> <p align=\"center\"> <a href=\"https://www.gnu.org/software/emacs/\"> <img src=\"https://img.shields.io/badge/GNU%20Emacs-" emacs-version "-b48ead.svg?style=plastic\"/></a> <a href=\"https://orgmode.org/\"><img src=\"https://img.shields.io/badge/org--mode-" org-version "-489a9f.svg?style=plastic\"/></a> </p> <p align=\"center\"> <img src=\"images/emacs-birthday-present.png\" width=250 height=250/> </p> ")) ;; My Literate Setup; need the empty new lines for the export " I enjoy reading others' /literate/ configuration files and incorporating what I learn into my own. The result is a sufficiently well-documented and accessible read that yields a stylish and functional system (•̀ᴗ•́)و This ~README.org~ has been automatically generated from my configuration and its contents below are accessible in (outdated) blog format, with /colour/, or as colourful PDF, [[https://alhassy.github.io/init/][here]]. Enjoy :smile: #+INCLUDE: init.org ") ;; No code execution on export ;; ⟪ For a particular block, we use “:eval never-export”. ⟫ (let ((org-export-use-babel nil)) (org-mode) (org-org-export-to-org)))
Alternatively, evaluate the above source block with C-c C-c
to produce a README
file.
For the ‘badges’, see https://shields.io/. The syntax above is structured:
https://img.shields.io/badge/<LABEL>-<MESSAGE>-<COLOR>.svg
‘Table of Contents’ for Org vs. Github
The above mentioned package toc-org, which creates an up-to-date table of
contents in an org file, at any heading tagged :TOC:
. It’s useful primarily for
README files on Github. There is also org-make-toc, which is more flexible: The
former provides only a top-level TOC; whereas this package allows TOCs at the
sibling level, say, to produce a TOC of only the subsections of a particular
heading, and other TOC features. Unlike toc-org, org-make-toc uses property drawers
to designate TOC matter.
(use-package toc-org ;; Automatically update toc when saving an Org file. :hook (org-mode . toc-org-mode) ;; Use both “:ignore_N:” and ":export_N:” to exlude headings from the TOC. :custom (toc-org-noexport-regexp "\\(^*+\\)\s+.*:\\(ignore\\|noexport\\)\\([@_][0-9]\\)?:\\($\\|[^ ]*?:$\\)"))
However, toc-org produces broken links for numbered sections.
That is, if we use #+OPTIONS: num:t
then a section, say
** =~/.emacs= vs. =init.org=
as the first subheading of the third
heading, then it renders with the text preceeded by 3.1
.
On the left-most part of the heading, Github provides a a link option;
clicking provides a link to this exact location in the README,
changing the current URL to something like
https://github.com/alhassy/emacs.d#31-emacs-vs-initorg
.
Now, toc-org produces Github-style anchors from Org headings,
but does not account for numbers, and so gives us
https://github.com/alhassy/emacs.d#emacs-vs-initorg
, which is
so close but missing the translated number, 31
.
I’ve experimented with using toc-org links using org-style, instead of the default Github style, but it seems that the org-style completely breaks rendering the resulting readme. Likewise, it seems that headings that are links break the TOC link; whence my section on the Reveal slide-deck system has a broken link to it. Perhaps org-make-toc solves these issues —something to look into.
I’m not sure how I feel about actually having the Github-serving TOC in my
source file. It’s nice to have around, from an essay-perspecive, but it breaks
HTML export since its links are not well-behaved; e.g., :ignore:
-ed headlines
appear in the toc, but do not link to any visible heading in the HTML; likewise,
headings with URLS in their names break. As such, below I’ve developed a way to
erase it altogether —alternatively, one could mark the toc as :noexport:
, but
this would then, in my current approach, not result in a toc in the resulting
README.
(cl-defun my/org-replace-tree-contents (heading &key (with "") (offset 0)) "Replace the contents of org tree HEADING with WITH, starting at OFFSET. Clear a subtree leaving first 3 lines untouched ⇐ :offset 3 Deleting a tree & its contents ⇐ :offset -1, or any negative number. Do nothing to a tree of 123456789 lines ⇐ :offset 123456789 Precondition: offset < most-positive-fixnum; else we wrap to a negative number." (interactive) (save-excursion (beginning-of-buffer) (re-search-forward (format "^\\*+ %s" (regexp-quote heading))) ;; To avoid ‘forward-line’ from spilling onto other trees. (org-narrow-to-subtree) (org-mark-subtree) ;; The 1+ is to avoid the heading. (dotimes (_ (1+ offset)) (forward-line)) (delete-region (region-beginning) (region-end)) (insert with) (widen))) ;; Erase :TOC: body ---provided we're using toc-org. ;; (my/org-replace-tree-contents "Table of Contents")
Alternate approaches to generating a README
Github supports several markup languages, one of which is Org-mode.
- It seems that Github uses org-ruby to convert org-mode to html.
- Here is a repo demonstrating how Github interprets Org-mode files.
- org-ruby supports inline
#+HTML
but not html blocks.
It seems coloured HTML does not render well:
(org-html-export-to-html) (shell-command "mv README.html README.md")
JavaScript supported display of web pages with:
#+INFOJS_OPT: view:info toc:t buttons:t
This looks nice for standalone pages, but doesn’t incorporate nicely with github README.org.
Usually, Github readme files are in markdown, which we may obtain from an Org
file with M-x org-md-export-to-markdown
.
- [ ] By default, this approach results in grey-coloured source blocks —eek!
- [X] It allows strategic placement of a table of contents.
Declare
#+options: toc:nil
at the top of the Org file, then have#+TOC: headlines 2
in a strategic position for a table of contents, say after a brief explanation of what the readme is for. - [X] It allows us to preview the readme locally before comitting, using grip.
;; grip looks for README.md (system-packages-ensure "grip") ;; Next: (async-shell-command "cd ~/.emacs.d/; grip")
We can approximate this behaviour for the other approaches:
- 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
.
Installing Emacs packages directly from source
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 :defer 5 :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"))
magit
—Emacs’ porcelain interface to gitq
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:
gRefresh the status buffer.
TABSee collapsed items, such as what text has been changed.
q
Quit magit, or go to previous magit screen.
s
Stage, i.e., add, a file to version control.
Add all untracked files by selecting the Untracked files title.
The staging area is akin to a pet store; commiting is taking the pet home.
k
Kill, i.e., delete a file locally.
K
This’ (magit-file-untrack)
which does git rm --cached
.
i
Add a file to the project .gitignore
file. Nice stuff =)
u
Unstage a specfif staged change highlighed by cursor.
C-u s
stages everything –tracked or not.
c
Commit a change.
- A new buffer for the commit message appears, you write it then
commit with
C-c C-c
or otherwise cancel 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
.
d
Show differences, another d
or another option.
- This is magit! Each hunk can be acted upon; e.g.,
s
orc
ork
;-)
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.
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 :defer t :custom ;; Do not ask about this variable when cloning. (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 :defer t)
If we want to roll back to a previous version, we just write-file
or C-x C-s
as
usual! The power of text!
Syncing to the System’s $PATH
For one reason or another, on OS X it seems that an Emacs instance
begun from the terminal may not inherit the terminal’s environment
variables, thus making it difficult to use utilities like pdflatex
when Org-mode attempts to produce a PDF.
(use-package exec-path-from-shell :init (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize)))
See the exec-path-from-shell documentation for setting other environment variables.
Installing OS packages, and automatically keeping my system up to data, from within Emacs
Sometimes Emacs packages depend on existing system binaries, use-package
let’s
us ensure these exist using the :ensure-system-package
keyword extension.
- This is like
:ensure t
but operates at the OS level and uses your default OS package manager.
Let’s obtain the extension.
;; Auto installing OS system packages (use-package use-package-ensure-system-package :defer 5 :config (system-packages-update)) ;; Ensure our operating system is always up to date. ;; This is run whenever we open Emacs & so wont take long if we're up to date. ;; It happens in the background ^_^ ;; ;; After 5 seconds of being idle, after starting up.
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 :defer t)
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 ^_^
“Being at the Helm” —Completion & Narrowing Framework
Whenever we have a choice to make from a list, Helm provides possible
completions and narrows the list of choices as we type. This is extremely
helpful for when switching between buffers, C-x b
, and discovering & learning
about other commands! E.g., press M-x
to see recently executed commands and
other possible commands! 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) ;; swoop doesn't work with PDFs, use Emacs' default isearch instead. :map pdf-view-mode-map ("C-s" . isearch-forward)) :custom (helm-swoop-speed-or-color nil "Give up colour for speed.") (helm-swoop-split-with-multiple-windows nil "Do not split window inside the current window."))
C-u 𝓃 C-s
does a search but showing 𝓃 contextual lines!helm-multi-swoop-all
,C-M-s
, lets us grep files anywhere!
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 ;-)
Having a workspace manager in Emacs
I’ve loved using XMonad as a window tiling manager. I’ve enjoyed the ability to segregate my tasks according to what ‘project’ I’m working on; such as research, marking, Emacs play, etc. With perspective, I can do the same thing :-)
That is, I can have a million buffers, but only those that belong to a workspace will be visible when I’m switching between buffers, for example. ( The awesome-tab and centaur-tab, mentioned elsewhere here, can be used to achieve the same thing by ‘grouping buffers together’. )
(use-package perspective :defer t :config ;; Activate it. (persp-mode) ;; In the modeline, tell me which workspace I'm in. (persp-turn-on-modestring))
All commands are prefixed by C-x x
; main commands:
s, n/→, p/←
‘S’elect a workspace to go to or create it, or go to ‘n’ext
one, or go to ‘p’revious one.
c
Query a perspective to kill.
r
Rename a perspective.
A
Add buffer to current perspective & remove it from all others.
As always, since we’ve installed which-key
, it suffices to press C-x x
then look
at the resulting menu
Excellent PDF Viewer
Let’s install the pdf-tools library for viewing PDFs in Emacs.
(use-package pdf-tools :defer t ; :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.
Who am I? —Using Gnus for Gmail
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 10 seconds, then start Gnus. ;; Gnus is slow upon startup since it fetches all mails upon startup. (run-with-idle-timer 10 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.
Prettifications
Let’s add the icon near my mail groups ^_^
;; Fancy icons for Emacs ;; Only do this once: (use-package all-the-icons :defer t) ; :config (all-the-icons-install-fonts 'install-without-asking) ;; Make mail look pretty (use-package all-the-icons-gnus :defer t :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"))
Super Terse Tutorial
⟨ See the GNUS Reference Card! ⟩
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”:(with-eval-after-load 'gnus (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
Capturing Mail as Todo/Notes
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.
(with-eval-after-load 'gnus (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.
Auto-completing mail addresses
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 :defer t :custom (gmail2bbdb-bbdb-file "~/Dropbox/bbdb")) (use-package bbdb :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.
Feeds to Blogs
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.
Jumping to extreme semantic units
Sometimes it’s unreasonable for M-<
to take us to the actual start of a buffer;
instead it’d be preferable to go to the first “semantic unit” in the buffer. For
example, when directory editing with dired
we should jump to the first file,
with version control with magit
we should jump to the first section, when
composing mail we should jump to the first body line, and in the agenda we
should jump to the first entry.
;; M-< and M-> jump to first and final semantic units. ;; If pressed twice, they go to physical first and last positions. (use-package beginend :diminish 'beginend-global-mode :config (beginend-global-mode) (loop for (_ . m) in beginend-modes do (diminish m)))
Hydra: Supply a prefix only once
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.
⇒ The Hydra Wiki has many example hydras for common uses cases ⇐
Below are two examples; one to simplify textual navigation and another for
window navigation.
Yet another possible hydra would be to avoid remembering word operations, such
as copying a word, upcasing it, killing a word from anywhere within it —in
contrast kill-word
kills to the end of the word—, etc. Likewise for line
operations, such as copying a line from anywhere in it. See Taking a tour of
one’s edits below for another small and useful example.
When there are multiple actions, it’s nice to see such a menu displayed in the middle of the frame; so we use hydra-posframe. Moreover, it can be useful to group related actions under a common heading —e.g., textual navigation may occur at the line level or word level or screen level— we obtain a nice interface by declaraing hydras using pretty-hydra-define —this saves us the trouble of formating docstrings using classic hydra.
;; Show hydras overlyaed in the middle of the frame (use-package hydra-posframe :quelpa (hydra-posframe :fetcher git :url "https://github.com/Ladicle/hydra-posframe.git") :hook (after-init . hydra-posframe-mode) :custom (hydra-posframe-border-width 5)) ;; Neato doc strings for hydras (use-package pretty-hydra)
Declaration template:
(pretty-hydra-define 𝓃𝒶𝓂ℯ (:title ⋯ :quit-key ⋯) (title₀ (⋯(key₀ᵢ command₀ᵢ optional-doc₀ᵢ)⋯) ⋮ titleₙ (⋯(keyₙᵢ commandₙᵢ optional-docₙᵢ)⋯)))
Textual Navigation —“Look Ma, no CTRL key!”
Basic movement commands —without having to hold the control key!
C-n
moves us to the next line, as it should; but it now also let’s us press
n,p,f,…
for other movement commands. Unlisted keys insert themselves, whereas q
close the pop-up menu.
(global-set-key (kbd "C-n") (pretty-hydra-define hydra-move (:body-pre (next-line) :title "\t\t\t\t\tTextual Navigation" :quit-key "q") ("Line" (("n" next-line) ("p" previous-line) ("a" beginning-of-line) ("e" move-end-of-line) ("g" goto-line)) "Word" (("f" forward-word "Next") ("b" backward-word "Previous") ("{" org-backward-element "Next Element") ("}" org-forward-element "Previous Element")) "Screen" (("v" scroll-up-command "Scroll Down") ("V" scroll-down-command "Scroll Up") ("l" recenter-top-bottom "Center Page") ("r" move-to-window-line-top-bottom "Relocate Point") ("m" helm-imenu "Textual Menu")))))
Window Navigation
It can be difficult to remember the incantations to adjust windows, so we can make a hydra to alleviate the trouble.
;; Use ijkl to denote ↑←↓→ arrows. (defhydra hydra-windows (global-map "C-c w" ) ("b" balance-windows "balance" ) ("i" enlarge-window "heighten") ("j" shrink-window-horizontally "narrow" ) ("k" shrink-window "lower" ) ("l" enlarge-window-horizontally "widen" ) ("s" switch-window-then-swap-buffer "swap" :color teal)) ;; Provides a *visual* way to choose a window to switch to. (use-package switch-window :defer t) ;; :bind (("C-x o" . switch-window) ;; ("C-x w" . switch-window-then-swap-buffer)) ;; Have a thick ruler between vertical windows (window-divider-mode)
Quickly pop-up a terminal, run a command, close it —and zsh
Pop up a terminal, do some work, then close it using the same command.
Shell-pop uses only one key action to work: If the buffer exists, and we’re in
it, then hide it; else jump to it; otherwise create it if it doesn’t exit. Use
universal arguments, e.g., C-u 5 C-t
, to have multiple shells and the same
universal arguments to pop those shells up, but C-t
to pop them away.
(use-package shell-pop :defer t :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 ;-)
Also, there’s tldr tool which aims to be like terse manuals for commandline-tools
in the style of practical example uses cases: tldr 𝒳
yields a number of ways
you’d actually use 𝒳.
(system-packages-ensure "tldr")
Restarting Emacs —Keeping buffers open across sessions?
Sometimes I wish to close then reopen Emacs; unsurprisingly someone’s thought of implementing that.
;; Provides only the command “restart-emacs”. (use-package restart-emacs :defer 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")
Automatic Backups
By default, Emacs saves backup files —those ending in ~
— in the current
directory, thereby cluttering it up. Let’s place them in ~~/.emacs.d/backups~, in
case we need to look for a backup; moreover, let’s keep old versions since
there’s disk space to go around —what am I going to do with 500gigs when nearly
all my ‘software’ is textfiles interpreted within Emacs
;; New location for backups. (setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) ;; Silently delete execess backup versions (setq delete-old-versions t) ;; Only keep the last 1000 backups of a file. (setq kept-old-versions 1000) ;; Even version controlled files get to be backed up. (setq vc-make-backup-files t) ;; Use version numbers for backup files. (setq version-control t)
Why backups? Sometimes I may forget to submit a file, or edit, to my version control system, and it’d be nice to be able to see a local automatic backup. Whenever ‘I need space,’ then I simply empty the backup directory, if ever. That the backups are numbered is so sweet ^_^
Like package installations, my backups are not kept in any version control system, like git; only locally.
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.
Screencapturing the Current Emacs Frame
Sometimes an image can be tremendously convincing, or at least sufficiently
inviting. The following incantation is written for MacOS and uses it’s native
screencapture
utility, as well as magick
.
(defun my/capture-emacs-frame (&optional prefix output) "Insert a link to a screenshot of the current Emacs frame. Unless the name of the OUTPUT file is provided, read it from the user. If PREFIX is provided, let the user select a portion of the screen." (interactive "p") (defvar my/emacs-window-id (s-collapse-whitespace (shell-command-to-string "osascript -e 'tell app \"Emacs\" to id of window 1'")) "The window ID of the current Emacs frame. Takes a second to compute, whence a defvar.") (let* ((screen (if prefix "-i" (concat "-l" my/emacs-window-id))) (temp (format "emacs_temp_%s.png" (random))) (default (format-time-string "emacs-%m-%d-%Y-%H:%M:%S.png"))) ;; Get output file name (unless output (setq output (read-string (format "Emacs screenshot filename (%s): " default))) (when (s-blank-p output) (setq output default))) ;; Clear minibuffer before capturing screen or prompt user (message (if prefix "Please select region for capture …" "♥‿♥")) ;; Capture current screen and resize (thread-first (format "screencapture -T 2 %s %s" screen temp) (concat "; magick convert -resize 60% " temp " " output) (shell-command)) (f-delete temp) ;; Insert a link to the image and reload inline images. (insert (concat "[[file:" output "]]"))) (org-display-inline-images nil t)) (bind-key* "C-c M-s" #'my/capture-emacs-frame)
Why this way? On MacOS, ImageMagick’s import
doesn’t seem to work —not at all
for me! Also, I dislike how large the resulting image is. As such, I’m using
MacOS’s screencapture
utility, which in-turn requires me to somehow obtain frame
IDs. Hence, the amount of work needed to make this happen on my system was most
simple if I just wrote it out myself rather than tweaking an existing system.
C-c C-x C-v
⇒ Toggle inline images!
Editor Documentation with Contextual Information
Emacs is an extensible self-documenting editor!
Let’s use a helpful Emacs documentation system that cleanly shows a lot of
contextual information —then let’s extend that to work as we want it to:
C-h o
to describe the symbol at point.
(use-package helpful :defer t) (defun my/describe-symbol (symbol) "A “C-h o” replacement using “helpful”: If there's a thing at point, offer that as default search item. If a prefix is provided, i.e., “C-u C-h o” then the built-in “describe-symbol” command is used. ⇨ Pretty docstrings, with links and highlighting. ⇨ Source code of symbol. ⇨ Callers of function symbol. ⇨ Key bindings for function symbol. ⇨ Aliases. ⇨ Options to enable tracing, dissable, and forget/unbind the symbol! " (interactive "p") (let* ((thing (symbol-at-point)) (val (completing-read (format "Describe symbol (default %s): " thing) (vconcat (list thing) obarray) (lambda (vv) (cl-some (lambda (x) (funcall (nth 1 x) vv)) describe-symbol-backends)) t nil nil)) (it (intern val))) (cond (current-prefix-arg (funcall #'describe-symbol it)) ((or (functionp it) (macrop it) (commandp it)) (helpful-callable it)) (t (helpful-symbol it))))) ;; Keybindings. (global-set-key (kbd "C-h o") #'my/describe-symbol) (global-set-key (kbd "C-h k") #'helpful-key)
I like helpful and wanted it to have the same behaviour as C-h o
, which
helpful-at-point
does not achieve. The incantation above makes C-h o
use helpful
in that if the cursor is on a symbol, then it is offered to the user as a
default search item for help, otherwise a plain search box for help
appears. Using a universal argument lets us drop to the built-in help command.
Cosmetics
Upon startup, we want to be greeted with a useful, yet unobtrusive, message
briefly detailing major system details. Moreover, the bottom-most area of
the screen should display 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
.
Startup message: Emacs & Org versions
Let’s always welcome ourselves when Emacs begins with a helpful message. For example, which user account is running and what are the version numbers of our primary tools.
;; Silence the usual message: Get more info using the about page via C-h C-a. (setq inhibit-startup-message t) (defun display-startup-echo-area-message () "The message that is shown after ‘user-init-file’ is loaded." (message (concat "Welcome " user-full-name "! Emacs " emacs-version "; Org-mode " org-version "; System " (symbol-name system-type) "/" (system-name) "; Time " (emacs-init-time))))
Now my startup message is,
;; Welcome Musa Al-hassy! Emacs 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 (•̀ᴗ•́)و"))
My to-do list: The initial buffer when Emacs opens up
I almost always have Emacs open; I don’t need a dashboard, but would like to see my to-do list and my init file, side-by-side.
(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.
Exquisite Themes
Emacs’ default theme leaves much to be desired: It does not look sleek and shiny, which usually leaves first-timers with a poor, shallow, impression of the system.
Below we install a few themes that make Emacs look exquisite.
We cycle between the chosen themes with C-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 :defer t) (use-package doom-themes :defer t) (use-package spacemacs-common :defer t :ensure spacemacs-theme)
- The Doom Themes also look rather appealing.
- A showcase of many themes can be found here.
;; Infinite list of my commonly used themes. (setq my/themes '(doom-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.
A sleek & informative mode line
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) ;; Use an arrow to seperate modeline information (powerline-default-separator 'arrow) ;; Show “line-number : column-number” in modeline. (spaceline-line-column-p t) ;; Use two colours to indicate whether a buffer is modified or not. (spaceline-highlight-face-func 'spaceline-highlight-face-modified) :config (custom-set-faces '(spaceline-unmodified ((t (:foreground "black" :background "gold"))))) (custom-set-faces '(spaceline-modified ((t (:foreground "black" :background "cyan"))))) (require 'spaceline-config) (spaceline-helm-mode) (spaceline-info-mode) (spaceline-emacs-theme))
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.
Powerful Directory Editing with dired
⟨ C-x C-v
to open a file or directory in dired, using the current buffer. ⟩
As mentioned earlier, dired
is Emacs’ built-in directory editor; it’s opened
with C-x d
. Dired let’s us treat directories as textual objects! In dired,
press h
to see the many actions available. Here’s a few:
(
toggles hiding entry details, such as modification date and ownerships
sort entries; modeline will display “Dired by date” or “Dired by name”.o
to open entry in anOther window; orRET
to open in place.+
to create a new directory; orM-x make-directory
./
to filter entries; withwhich-key
, possible completions pop-up.- E.g.,
/ f
shows only files or/ . png
to obtain all entries with extensionpng
. / i g
to hide git-ignored items ^_^/ /
to remove all filters.
- E.g.,
TAB
to navigate between different groupings of entries.RET
on a drawer heading toggles folding it ^_^
The dired-hacks family of packages lets us, say, get a dired buffer out of a shell incantation that lists files, or use dired to open files with external tools. Below we use three of its packages.
Pressing i
inserts a directory’s children under it, indented, in the current
buffer. Useful to see what’s there.
(use-package dired-subtree :bind (:map dired-mode-map ("i" . dired-subtree-toggle)))
When directory 𝒳
has only one child 𝒴
, then in dired, instead of 𝒳
, show me 𝒳/𝒴
with 𝒳
greyed out.
(use-package dired-collapse :hook (dired-mode . dired-collapse-mode))
Begin dired with certain entries grouped together, according to some filtering
requirement; and with “garbage” files not shown —i.e., those ending in
.aux, .out
, etc.
(use-package dired-filter :hook (dired-mode . (lambda () (dired-filter-group-mode) (dired-filter-by-garbage))) :custom (dired-garbage-files-regexp "\\(?:\\.\\(?:aux\\|bak\\|dvi\\|log\\|orig\\|rej\\|toc\\|out\\)\\)\\'") (dired-filter-group-saved-groups '(("default" ("Org" (extension "org")) ("Executables" (exexutable)) ("Directories" (directory)) ("PDF" (extension "pdf")) ("LaTeX" (extension "tex" "bib")) ("Images" (extension "png")) ("Code" (extension "hs" "agda" "lagda")) ("Archives"(extension "zip" "rar" "gz" "bz2" "tar"))))))
Neotree: Traditional Directory Tree Navigation
⟨ Disabled ⟩
We open a nifty file manager upon startup.
;; Sidebar for project file navigation (use-package neotree :defer t :disabled :config (global-set-key "\C-x\ d" 'neotree-toggle) (setq neo-theme 'icons)) ;; Uses all-the-icons from § Booting Up ;; Open it up upon startup. ;; (neotree-toggle)
By default C-x d
invokes dired
, but I prefer neotree
for file
management.
⟨ Edit: As a naive user, this is what I thought; yet a year later, I’ve almost never used neotree. ⟩
Useful navigational commands include
U
to go up a directory.C-c C-c
to change directory focus;C-C c
to type the directory out.?
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.
Never lose the cursor
Let’s have the entire line containing the cursour be slightly highlighted.
;; Make it very easy to see the line with the cursor. (global-hl-line-mode t)
Moreover, we reduce the mental strain of locating the cursour when navigation happens: When we switch windows or scroll, for example, we get a wave of light near the cursor.
(use-package beacon :diminish :config (setq beacon-color "#666600") :hook ((org-mode text-mode) . beacon-mode))
Dimming Unused Windows
Let’s dim windows, and even the whole Emacs frame, when not in use.
(use-package dimmer :config (dimmer-mode))
A more ‘fine-grained’ tool dims all text except the ‘paragraph’ you’re working on. It’s nifty, but not for me.
Buffer names are necessarily injective
By default when multiple files sharing the same name are opened, say for comparison from different directories, their buffers are named uniquely by having the format “⟨file-name⟩ <𝓃>”, for numbers 𝓃. It’d be more helpful to have the buffer names reflect their location.
;; Note that ‘uniquify’ is builtin. (require 'uniquify) (setq uniquify-separator "/" ;; The separator in buffer names. uniquify-buffer-name-style 'forward) ;; names/in/this/style
Note that this does not affect cloning buffers, 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! )
Flashing when something goes wrong —no blinking
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)
Hiding Scrollbar, tool bar, and menu
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 & complete parenthesis pair when cursor is near ;-)
Highlight matching ‘parenthesis’ when near one of them.
(setq show-paren-delay 0) (setq show-paren-style 'mixed) (show-paren-mode)
Colour parens, and other delimiters, depending on their depth. Very useful for parens heavy languages like Lisp.
(use-package rainbow-delimiters :disabled :hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode))
For example:
(blue (purple (forest (green (yellow (blue))))))
There is a powerful package called ‘smartparens’ for working with pair-able
characters, but I’ve found it to be too much for my uses. Instead I’ll utilise
the lightweight package electric
, which Emacs provides out of the box.
(electric-pair-mode 1)
It supports, by default, ACSII pairs {}, [], ()
and Unicode ‘’, “”, ⟪⟫, ⟨⟩
.
When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed and considered as pairs. Let’s disassociate them from both notions.
;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition. (setq electric-pair-inhibit-predicate (lambda (c) (or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c)))) ;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’. (modify-syntax-entry ?< "w<") (modify-syntax-entry ?> "w>")
Adding Org-emphasise markers for pair completion —Disabled.
Let’s add the org-emphasises markers: If we select a word then press *
, it
becomes bold; likewise for /
for emphasise.
(setq electric-pair-pairs '((?~ . ?~) (?* . ?*) (?/ . ?/))) ;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file. ;; Disable pairs when entering minibuffer (add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0))) ;; Renable pairs when existing minibuffer (add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1)))
I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous Org headers, for which the ‘completed closing pair’ must tiresomely be deleted.
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.
Window resizing using the golden ratio
Let’s load the following package, which automatically resizes windows so that the window containing the cursor is the largest, according to the golden ratio. Consequently, the window we’re working with is nice and large yet the other windows are still readable.
(use-package golden-ratio :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.
Persistent Scratch Buffer
The *scratch*
buffer is a nice playground for temporary data or experiments.
However, by default its contents are not saved –which may be an issue if we have not relocated our playthings to their appropriate files. Whence let’s save & restore the scratch buffer by default.
(use-package persistent-scratch :defer t ;; In this mode, the usual save key saves to the underlying persistent file. :bind (:map persistent-scratch-mode-map ("C-x C-s" . persistent-scratch-save)))
We might accidentally close this buffer, so we could utilise the following.
(defun scratch () "Recreate the scratch buffer, loading any persistent state." (interactive) (switch-to-buffer-other-window (get-buffer-create "*scratch*")) (condition-case nil (persistent-scratch-restore) (insert initial-scratch-message)) (org-mode) (persistent-scratch-mode) (persistent-scratch-autosave-mode 1)) ;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch* ;; Upon startup, close the default scratch buffer and open one as specfied above (ignore-errors (kill-buffer "*scratch*") (scratch))
I use Org-mode often, so that’s how I want things to appear.
(setq initial-scratch-message (concat "#+Title: Persistent Scratch Buffer" "\n#\n# Welcome! This’ a place for trying things out." "\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n"))
Prose
Emacs can be setup with a spellchecker and other expected features of a word processing tool —however these features apply Emacs-wide since nearly everything is essentially text (•̀ᴗ•́)و
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
Fill-mode —Word Wrapping
In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting paragraphs. This is a powerful form of “word wrap”.
(setq-default fill-column 80 ;; Let's avoid going over 80 columns truncate-lines nil ;; I never want to scroll horizontally indent-tabs-mode nil) ;; Use spaces instead of tabs
Certain variables are sensibly local to a buffer, and so setq
only alters their
value for one buffer. Using setq-default
we change a variable’s default value,
in every buffer.
;; Wrap long lines when editing text (add-hook 'text-mode-hook 'turn-on-auto-fill) (add-hook 'org-mode-hook 'turn-on-auto-fill) ;; Do not show the “Fill” indicator in the mode line. (diminish 'auto-fill-function)
We may press M-q
to cleverly redistribute the line breaks within any paragraph,
thereby making it look better. With a prefix argument, it justifies it as well
—i.e., pads extra white space to make the paragraph appear rectangular.
Note that M-o M-s
centres a line of text ;-) Fun stuff!
Fill-mode is also known as “hard word wrapping”, which has the counterpart “soft word wrapping” …
Visual line mode is built-in and provides support for editing by visual lines:
Lines off the screen are visually word wrapped, but logically remain one line.
Moreover C-a,e,k
operate on visual lines rather than logical lines.
;; Bent arrows at the end and start of long lines. (setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow)) (diminish 'visual-line-mode) (global-visual-line-mode 1)
Visual line mode is useful when I have way too many windows open or when using smaller frames.
Pretty Lists Markers
When writing, it’s common to use +,-,*
to enumerate unordered lists
—especially so in Org-mode wherein they denote structured text. Let’s render
them visually as Unicode bullets.
;; (x y z) ≈ (existing-item replacement-item positivity-of-preceding-spaces) (require 'cl) (loop for (x y z) in '(("+" "◦" *) ("-" "•" *) ("*" "⋆" +)) do (font-lock-add-keywords 'org-mode `((,(format "^ %s\\([%s]\\) " z x) (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ,y)))))))
Word Completion
Let’s enable “complete anything” mode —it ought to start in half a second and only need two characters to get going, which means word suggestions are provided and so I need only type partial words then tab to get the full word!
(use-package company :diminish :config (global-company-mode 1) (setq ;; Only 2 letters required for completion to activate. company-minimum-prefix-length 2 ;; Search other buffers for compleition candidates company-dabbrev-other-buffers t company-dabbrev-code-other-buffers t ;; Show candidates according to importance, then case, then in-buffer frequency company-transformers '(company-sort-by-backend-importance company-sort-prefer-same-case-prefix company-sort-by-occurrence) ;; Flushright any annotations for a compleition; ;; e.g., the description of what a snippet template word expands into. company-tooltip-align-annotations t ;; Allow (lengthy) numbers to be eligible for completion. company-complete-number t ;; M-⟪num⟫ to select an option according to its number. company-show-numbers t ;; Show 10 items in a tooltip; scrollbar otherwise or C-s ^_^ company-tooltip-limit 10 ;; Edge of the completion list cycles around. company-selection-wrap-around t ;; Do not downcase completions by default. company-dabbrev-downcase nil ;; Even if I write something with the ‘wrong’ case, ;; provide the ‘correct’ casing. company-dabbrev-ignore-case nil ;; Immediately activate completion. company-idle-delay 0) ;; Use C-/ to manually start company mode at point. C-/ is used by undo-tree. ;; Override all minor modes that use C-/; bind-key* is discussed below. (bind-key* "C-/" #'company-manual-begin) ;; Bindings when the company list is active. :bind (:map company-active-map ("C-d" . company-show-doc-buffer) ;; In new temp buffer ("<tab>" . company-complete-selection) ;; Use C-n,p for navigation in addition to M-n,p ("C-n" . (lambda () (interactive) (company-complete-common-or-cycle 1))) ("C-p" . (lambda () (interactive) (company-complete-common-or-cycle -1))))) ;; It's so fast that we don't need a key-binding to start it!
Note that M-/
goes through a sequence of completions —and C-/
manually begins
company mode at point. Besides the arrow keys, we can also use M-
with n, p
to
navigate the options or use C-s
to search the list of suggestions.
- Company backends are available as separate packages.
- Note that by default company mode does not support completion for phrases containing hyphens —this can be altered, if desired.
Besides boring word completion, let’s add support for emojis.
(use-package company-emoji :config (add-to-list 'company-backends 'company-emoji))
For example: .
On a new line, write :
then any letter to have a tool-tip appear.
All emoji names are lowercase.
- On MacOS,
C-⌘-SPC
brings up an emoji picker, where one drags desired emojis to textual areas. - Here is a list of emoji —all supported by Github.
The libraries emojify
and emojify-logos
provides cool items like =:haskell:
with org-mode, so I’m not using them.
Fix spelling as you type —thesaurus & dictionary too!
I would like to check spelling on the fly.
C-;
Cycle through corrections for word at point.
M-$
Check and correct spelling of the word at point
M-x ispell-change-dictionary RET TAB
To see what dictionaries are available.
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.
Touch Typing
Use this game to help you learn to spell words that you’re having trouble with;
e.g., I have a file ~~/Dropbox/spelling.txt~ with words I have trouble spelling,
which I open then run M-x typing-of-emacs
in order to improve spelling said
words.
;; The Typing Of Emacs, a game. (use-package typing-of-emacs :defer t :quelpa (typing :fetcher wiki :url "https://www.emacswiki.org/emacs/typing.el"))
Practice touch typing using speed-type.
(use-package speed-type :defer t)
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 :defer t :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
Using a Grammar & Style Checker
Let’s install a grammar and style checker. We get the offline tool from the bottom of the LanguageTool website, then relocate it as follows.
(use-package langtool :defer t :custom (langtool-language-tool-jar "~/Applications/LanguageTool-4.5/languagetool-commandline.jar"))
Now we can run langtool-check
on the subsequent grammatically incorrect
text —which is from the LanguageTool website— which colours errors in red,
when we click on them we get the reason why; then we may invoke
langtool-correct-buffer
to quickly use the suggestions to fix each correction,
and finally invoke langtool-check-done
to stop any remaining red colouring.
LanguageTool offers spell and grammar checking. Just paste your text here and click the 'Check Text' button. Click the colored phrases for details on potential errors. or use this text too see an few of of the problems that LanguageTool can detecd. What do you thinks of grammar checkers? Please not that they are not perfect. Style issues get a blue marker: It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017 --uh oh, that's the wrong date ;-)
By looking around the source code, I can do all three stages smoothly (•̀ᴗ•́)و
;; Quickly check, correct, then clean up /region/ with M-^ (eval-after-load 'langtool (progn (add-hook 'langtool-error-exists-hook (lambda () (langtool-correct-buffer) (langtool-check-done))) (global-set-key "\M-^" (lambda () (interactive) (message "Grammar checking begun ...") (langtool-check)))))
The checking command is silent, we added a bit of comforting acknowledgement to the user.
Lightweight Prose Proofchecking
Let’s write good!
(use-package writegood-mode ;; Load this whenver I'm composing prose. :hook (text-mode org-mode) ;; Don't show me the “Wg” marker in the mode line :diminish ;; Some additional weasel words. :config (--map (push it writegood-weasel-words) '("some" "simple" "simply" "easy" "often" "easily" "probably" "clearly" ;; Is the premise undeniably true? "experience shows" ;; Whose? What kind? How does it do so? "may have" ;; It may also have not! "it turns out that"))) ;; How does it turn out so? ;; ↯ What is the evidence of highighted phrase? ↯
Inspired by Matt Might’s 3 shell scripts to improve your writing, or “My Ph.D. advisor rewrote himself in bash”, this Emacs interface emphasises, via underline, the following weaknesses in writing —so that I can fix them or decide that they are appropriate for the scenario.
Sentences that cut out the following problems may become stronger —by being more terse or precise.
Weasel Words Phrases that sound good without conveying information; such as vague precision or subjective phrases.
E.g., a number of, surprisingly, very close.
It’s okay not to have exact details, but rather than “I don’t know” explain why not and what the next steps will be.
Passive Voice Phrases wherein interest is in the object experiencing an action, rather than the subject that performs the action.
- Bad: The house was built by my father.
- Good: My father built this house.
Likewise, including relevant or explanatory information as in “X guarantees Y” is an improvement over “Y is guaranteed”.
Sometimes the subject really is irrelevant, such as “We did X” whereas “X happened” suffices.
If the relevant subject is unclear and, also, the text reads better in the active, then change a phrase.
Duplicated WordsOccurrences of, say, “the the”.
Harder to catch manually, but easier mechanically ;-)
Placeholder Text —For Learning & Experimenting
When learning about Emacs formatting commands, such as zap-to-char M-z
or transpose M-t
, it’s best to have filler text —even better when
it’s automatically generated instead of typing it out ourselves. The
following will give us a series of commands lorem-ipsum-insert-⋯
for
inserting lists, sentences, paragraphs and using a prefix argument,
with C-u
, we can request to generate any number of them.
(use-package lorem-ipsum :defer t)
‘Lorem’ is not a word itself, but it comes from the Latin ‘Dolorem Ipsum’ which means “pain in and of itself”.
See this Emacs Cheat Sheet to try out the textual navigation and formatting bindings on lorem ipsum, gibberish text.
Some text to make us smile
The dad-joke queries https://icanhazdadjoke.com to bring us some funny.
(use-package dad-joke :defer t :config (defun dad-joke () (interactive) (insert (dad-joke-get))))
For example, M-x dad-joke
now inserts:
What are the strongest days of the week? Saturday and Sunday…the rest are weekdays.
Unicode Input via Agda Input
Agda is one of my favourite languages, it’s like Haskell on steroids. Let’s set it up for the main sake of its Unicode input —you may do likewise using TeX input. ( The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) )
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.
(use-package agda-input :ensure nil ;; I have it locally. :demand t :hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda"))) :custom (default-input-method "Agda")) ;; Now C-\ or M-x toggle-input-method turn it on and offers
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" "⌈⌉") ;; Arrows ("<=" "⇐") ;; 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
.
Increase/decrease text size
The ‘usual’ text zoom keys C-±
…
(global-set-key (kbd "C-+") 'text-scale-increase) (global-set-key (kbd "C--") 'text-scale-decrease) ;; C-x C-0 restores the default font size
If thou knowst the ELisp, forgive this shadowing of the negative-argument
… we’ve still got M--
though.
Curious, this is one of the very first things I did when began using Emacs; yet, perhaps I would not have done it if I was simply told the defaults:
C-x C-=,+
increases text 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.
Moving Text Around
This extends Org-mode’s M-↑,↓
to other modes, such as when coding.
;; M-↑,↓ moves line, or marked region; prefix is how many lines. (use-package move-text :config (move-text-default-bindings))
Enabling CamelCase Aware Editing Operations
Subword movement lets us treat “EmacsIsAwesome” as three words
─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming
is common among coders. Now, for example, M-f
moves along each subword.
(global-subword-mode 1) (diminish 'subword-mode)
Mouse Editing Support
Text selected with the mouse is automatically copied to clipboard.
(setq mouse-drag-copy-region t)
Delete Selection Mode
Delete Selection mode lets you treat an Emacs region much like a typical text selection outside of Emacs: You can replace the active region. We can delete selected text just by hitting the backspace key.
(delete-selection-mode 1)
M-n,p
: Word-at-Point Navigation
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 :defer t :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)
Letter-based Navigation
At a glance of possible positions, across windows, and a key to jump there is a feature provided to us by ace-jump —here is an emacs-rocks 2-minute video.
For example, C-c SPC m
greys our all windows and places a red
letter at the start of any word that begins with m, then I may
press a letter to jump to the associated position in the
associated window. Using C-u C-c SPC
and C-u C-u C-c SPC
let
me jump to any character or to any visible line.
➩ Super simple use case: Fix your eyes on an occurence of a word, then C-c SPC
to quickly jump to it so as to edit the sentence in which it occurs.
- It’s like
C-s
but more lightweight.
(use-package ace-jump-mode :defer t :config (bind-key* "C-c SPC" 'ace-jump-mode)) ;; See ace-jump issues to configure for use of home row keys.
There is a newer and somewhat more powerful package, avy, which accompishes the same goal. It uses a tree style to jumipng: Locations are given two letter combinations, one presses one letter to jump to a group of text, then another letter to jump somewhere in that grouping. I prefer ace-jump since it greys everthing out, whereas avy surrounds jump locations with a box. Here is an emacs-doom 6-minute video for avy.
There is also ace-isearch for bridinging different navgiational methods —one
begins incremental search, s-f
, then according to a pause and length of input,
one of the navgiational methods, such as isearch or avy or helm-swoop, will be
begun. I’m okay with using C-s
for helm-swoop and C-c SPC
for ace-jump, and
still have s-f
for incremental search, which I hardly use.
What is bind-keys*?
Major modes provide specfic use and so their bindings always take precedence
over global bindings —e.g., the major mode binding may do what the global does
but with extra mode-specfic behaviour, such as indentation. Other times, a major
mode’s binding simply uses the same key presses with completely unrelated
behaviour. If we want to avoid having our global keybindings shadowed by a
major mode, we may use the bind-key*
macro of use-package
, or the bind-keys*
macro when there are multiple keys; these are macros, not clauses. —These
essentially creates a dedicated minor mode behind the scenes, which saves us the
work of doing it ourselves.
(bind-keys* (k₁ . f₁) … (kₙ . fₙ)) | |
≈ | These keybindings override all minor modes that use keys kᵢ . |
C-c e n,p
: Taking a tour of one’s edits
This package allows us to move around the edit points of a buffer
without actually undoing anything. We even obtain a brief description
of what happend at each edit point.
This seems useful for when I get interrupted or lose my train of
thought: Just press C-c e p
to see what I did recently and where
—the “e” is for “e”dit.
;; Give me a description of the change made at a particular stop. (use-package goto-chg :defer t :custom (glc-default-span 0)) (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.
Org-Mode Administrivia
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)
Executing code from src
blocks
For example, to execute a shell command in Emacs, write a src
with a shell
command, then C-c c-c
to see the results. Emacs will generally query you to
ensure you’re confident about executing the (possibly dangerous) code block;
let’s stop that:
;; Seamless use of babel: No confirmation upon execution. ;; Downside: Could accidentally evaluate harmful code. (setq org-confirm-babel-evaluate nil)
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
.
High Speed Literate Programming
Manipulating Sections
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.
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!g
to go to another heading, without refiling anything.
;; Refile anywhere 1-level deep in current file, ;; or anywhere in my agenda files 2-levels deep (setq org-refile-targets '((nil . (:maxlevel . 1)))) ;; (org-agenda-files . (:maxlevel . 2)) ;; ← Not useful right now. ; Use full outline paths for refile targets - we file directly with IDO ;; When refiling, using Helm, show me the hierarchy paths (setq org-outline-path-complete-in-steps nil) (setq org-refile-use-outline-path 'file-path)
t/,/:/e
to add a TODO state, priority level, tag, or effort estimate^
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.u
for going to upwards to parent headingD/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.k/@/a
to kill or mark or archive the current subtreeo
to open a link mentioned in the subtree then go to the link; a pop-up of links appears.
We can add our own speed keys by altering the org-speed-commands-user
association list variable.
⇒ Moreover, ?
to see a complete list of keys available. ⇐
(setq org-use-speed-commands t)
Seamless Navigation Between Source Blocks
The “super key” —aka the command or windows key— can be used to jump to the previous, next, or toggle editing org-mode source blocks.
;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill. ;; (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”.
Modifying <return>
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 |
C-a,e,k
and Yanking of sections
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)
Hiding Emphasise Markers & Inlining Images
Let’s make some things prettier than they appear by default.
;; 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
Org pretty entities seems rather impressive —=M-x org-entities-help= to see all possibilities, or add your own. I’m already using the Agda input method, so I wont use Org’s —Agda’s gives me a tiny menu narrowing possibilities as I type.
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)
Org-Emphasise for Parts of Words
From stackoverflow, the following incantation allows us to have parts of works emphasied with org-mode; e.g., /half/ed, ~half~ed, and right in the m*idd*le! Super cool stuff!
(setcar org-emphasis-regexp-components " \t('\"{[:alpha:]") (setcar (nthcdr 1 org-emphasis-regexp-components) "[:alpha:]- \t.,:!?;'\")}\\") (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components)
I’ve disabled this feature since multiple occurrences of an emphasise marker are sometimes treated as one lengthy phrase being emphasised.
Proportional fonts for Headlines
Let’s have headings stick out a bit.
- The larger headings are cute and reminicint of word processors, but having headings coloured is enough —the larger size is too much.
(set-face-attribute 'org-document-title nil :height 2.0) ;; (set-face-attribute 'org-level-1 nil :height 1.0) ;; Remaining org-level-𝒾 have default height 1.0, for 𝒾 : 1..8. ;; ;; E.g., reset org-level-1 to default. ;; (custom-set-faces '(org-level-1 nil))
Remember you can always use Emacs’ Custom utility to get Lisp incantations ;-) —See notes on Custom above.
Show off-screen heading at the top of the window
In case we forgot which heading we’re under, let’s keep the current heading stuck at the top of the window.
(use-package org-sticky-header :hook (org-mode . org-sticky-header-mode) :config (setq-default org-sticky-header-full-path 'full ;; Child and parent headings are seperated by a /. org-sticky-header-outline-path-separator " / "))
Jumping without hassle
(defun my/org-goto-line (line) "Go to the indicated line, unfolding the parent Org header. Implementation: Go to the line, then look at the 1st previous org header, now we can unfold it whence we do so, then we go back to the line we want to be at. " (interactive "nEnter line: ") (goto-line line) (org-previous-visible-heading 1) (org-cycle) (goto-line line))
Folding within a subtree
(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)))
Making Block Delimiters Less Intrusive
Let us render Org-mode’s #+begin_src
and #+end_src
less obtrusively by, e.g.,
having the former render as a pencil marker ✎ and the latter as a tombstone □
—reminiscent of Halmos’ QED end-of-proof marker. 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 '(;; Example of how pairs like this to beautify org block delimiters ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒ ("#+end_example" . ?⇐) ;; ⇐ ;; Actuall beautifications ("<=" . ?≤) (">=" . ?≥) ("->" . ?→) ("-->". ?⟶) ;; threading operators ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols 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)
Org-mode’s <𝒳
Block Expansions
In org-mode we type <X TAB
to obtain environment templates, such as <s
for
source blocks or <q
for quote blocks. It seems recent changes to the org-mode
structure template expansion necessitate explicitly loading org-tempo
.
(require 'org-tempo)
To insert source blocks with the assistance of a pop-up: C-c C-v d
;-)
Perhaps more usefully, invoking within a source block splits it up into two
separate blocks! Moreover, if invoked on a selected region, it puts the region
into a new code block! Wow!
C-c C-,~ refers to ~org-insert-structure-template
, which provides non-source blocks, such as quote<q
, comment<C
, center<c
, notes<n
, examples<e
, and<l
and<h
and<a
for LaTeX and HTML and ASCII export blocks.<X
allows you to obtain the org-block assigned to 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.
Executing all #+name: startup-code
for local configurations
Sometimes my Org-files contain configurations that are local to the file,
so I name all such src
blocks #+name: startup-code
and place #-*- eval: (my/execute-startup-blocks) -*-
at the top of the file so that such
blocks are evaluated when the file opens up.
- The
-*- ... -*-
notation is for making local configurations. - Use
M-x add-file-local-variable-prop-line
to have them inserted interactively.
(defun my/execute-startup-blocks () "Execute all startup blocks, those named ‘startup-code’." (interactive) (save-excursion (goto-char 0) (while (ignore-errors (re-search-forward "^\\#\\+name: startup-code")) (org-babel-execute-src-block))))
The following setup enables this feature in a safe fashion —e.g., we do not want to avoid evaluating a random person’s potentially dangerous code when we only want to look at it.
;; Please ask me on a file by file basis whether its local variables are ‘safe’ ;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried ;; again for the same file. (setq enable-local-variables t)
I have been using a combination of (org-babel-goto-named-src-block ⋯)
in
multi-line local-variable declarations —=M-x add-file-local-variable-prop=—
for a while in many files using a dedicated * footer :noexport:
section, but
this new approach frees from having such sections and instead to having a single
line at the top of the file. Moreover, being at the top of the file, such a line
is a nice ‘in your face’ reminder that there is local configuration that should
have been loaded.
- E.g., this init file has local configuration for making the corresponding
init.el
file and generating theREADME.org
file.
Org-Mode ⇒ PDF & HTML
In this section we consider the Org-mode exporters for PDFs and HTMLs. For example, we account for LaTeX citations and reliable HTML anchors.
Working with Citations
An exquisite system for handling references.
The following entity will display useful data when the mouse hovers over it (•̀ᴗ•́)و If you click on it, then you’re in for a lot of super neat stuff, such as searching for the pdf online!
cite:agda_overview
(use-package org-ref :custom ;; Files to look at when no “╲bibliography{⋯}” is not present in a file. ;; Most useful for non-LaTeX files. (reftex-default-bibliography '("~/thesis-proposal/papers/References.bib")) (bibtex-completion-bibliography (car reftex-default-bibliography)) (org-ref-default-bibliography reftex-default-bibliography)) ;; Quick BibTeX references, sometimes. (use-package helm-bibtex) (use-package biblio)
Execute M-x helm-bibtex
or C-c ]
and, say, enter emacs
and you will be
presented with all the entries in the bib database that mention ‘emacs’. Super
cool stuff. Moreover, if no such entries exist, then we can look some up
using the interface!
Read the manual online or better yet as an org-file with M-x org-ref-help
.
This is an Org-mode application since the citations have tooltips and export nicely to LaTeX & HTML via the Org-mode exporter.
Bibliography & Coloured LaTeX using Minted
Execute the following for bibliography references as well as minted Org-mode uses the Minted package for source code highlighting in PDF/LaTeX —which in turn requires the pygmentize system tool.
(setq org-latex-listings 'minted org-latex-packages-alist '(("" "minted")) org-latex-pdf-process '("pdflatex -shell-escape -output-directory %o %f" "biber %b" "pdflatex -shell-escape -output-directory %o %f" "pdflatex -shell-escape -output-directory %o %f"))
For faster pdf generation, possibly with errors, consider invoking:
(setq org-latex-pdf-process '("pdflatex -interaction nonstopmode -output-directory %o %f"))
By default, Org exports LaTeX using the nonstopmode
option, which tries
its best to produce a PDF —which ignores typesetting errors altogether,
which is not necessary ideal when using LaTeX.
Ensuring Useful HTML Anchors
Upon HTML export, each tree heading is assigned an ID to be used for hyperlinks.
Default IDs are something like org1957a9d
, which does not endure the test of time:
Re-export will produce a different id. Here’s a rough snippet to generate
IDs from headings, by replacing spaces with hyphens, for headings without IDs.
(defun my/ensure-headline-ids (&rest _) "Org trees without a All non-alphanumeric characters are cleverly replaced with ‘-’. If multiple trees end-up with the same id property, issue a message and undo any property insertion thus far. E.g., ↯ We'll go on a ∀∃⇅ adventure ↦ We'll-go-on-a-adventure " (interactive) (let ((ids)) (org-map-entries (lambda () (org-with-point-at (point) (let ((id (org-entry-get nil "CUSTOM_ID"))) (unless id (thread-last (nth 4 (org-heading-components)) (s-replace-regexp "[^[:alnum:]']" "-") (s-replace-regexp "-+" "-") (s-chop-prefix "-") (s-chop-suffix "-") (setq id)) (if (not (member id ids)) (push id ids) (message-box "Oh no, a repeated id!\n\n\t%s" id) (undo) (setq quit-flag t)) (org-entry-put nil "CUSTOM_ID" id)))))))) ;; Whenever html & md export happens, ensure we have headline ids. (advice-add 'org-html-export-to-html :before 'my/ensure-headline-ids) (advice-add 'org-md-export-to-markdown :before 'my/ensure-headline-ids)
One may then use [[#my-custom-id]]
to link to the entry with CUSTOM_ID
property my-custom-id
.
Interestingly, org-set-property
, C-c C-x p
, lets us insert a property
from a selection of available ones, then we’ll be prompted for a value
for it from a list of values you’ve used elsewhere. This is useful for
remaining consistent for when trees share similar properties.
Clickable Headlines
By default, HTML export generates ID’s to headlines so they may be referenced to, but there is no convenient way to get at them to refer to a particular heading. The following spell fixes this issue: Headlines are now clickable, resulting in a link to the headline itself.
;; Src: https://writepermission.com/org-blogging-clickable-headlines.html (setq org-html-format-headline-function (lambda (todo todo-type priority text tags info) "Format a headline with a link to itself." (let* ((headline (get-text-property 0 :parent text)) (id (or (org-element-property :CUSTOM_ID headline) (org-export-get-reference headline info) (org-element-property :ID headline))) (link (if id (format "<a href=\"#%s\">%s</a>" id text) text))) (org-html-format-headline-default-function todo todo-type priority link tags info))))
HTML “Folded Drawers”
(defun my/org-drawer-format (name contents) "Export to HTML the drawers named with prefix ‘fold_’, ignoring case. The resulting drawer is a ‘code-details’ and so appears folded; the user clicks it to see the information therein. Henceforth, these are called ‘fold drawers’. Drawers without such a prefix may be nonetheless exported if their body contains ‘:export: t’ ---this switch does not appear in the output. Thus, we are biased to generally not exporting non-fold drawers. One may suspend export of fold drawers by having ‘:export: nil’ in their body definition. Fold drawers naturally come with a title. Either it is specfied in the drawer body by ‘:title: ⋯’, or otherwise the drawer's name is used with all underscores replaced by spaces. " (let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents)) (fold? (s-prefix? "fold_" name 'ignore-case)) (export? (string-match ":export:\s+t" contents)) (not-export? (string-match ":export:\s+nil" contents)) (title′ (and (string-match ":title:\\(.*\\)\n" contents) (match-string 1 contents)))) ;; Ensure we have a title. (unless title′ (setq title′ (s-join " " (cdr (s-split "_" name))))) ;; Output (cond ((and export? (not fold?)) contents′) (not-export? nil) (fold? (thread-last contents′ (replace-regexp-in-string ":title:.*\n" "") (format "<details class=\"code-details\"> <summary> <strong> <font face=\"Courier\" size=\"3\" color=\"green\"> %s </font> </strong> </summary> %s </details>" title′)))))) (setq org-html-format-drawer-function 'my/org-drawer-format)
With the following invocations we only see the odd indexed ‘hello’s, where the latter two are folded up.
:this-drawer-is-exported: :export: t hello 1 :End: :this-drawer-is-NOT-exported: hello 2 :End: :fold_This_drawer_has_a_title_in_the_body: :title: I am the drawer title 0 hello 3 :End: :fold_This_drawer_is_NOT_exported: :title: Why are we here? :export: nil hello 4 :End: :fold_I_am_the_drawer_title_1: hello 5 :End:
I doubt I could show an example in the Github README, since no HTML export is happening using my setup. In case you’re reading this on my blog, which has exported HTML. Here’s the example: Now that I’ve written this, I’m thinking it may have been preferably to use an org-block…?
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 :defer t :custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"))
For example, execute, C-x C-e
after the closing parenthesis of, the
following block to see an example slide-deck (─‿‿─)
(progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org") (switch-to-buffer (find-file "Trying_out_reveal.org")) (org-reveal-export-to-html-and-browse))
Org-mode exporting, C-c C-e
, now includes an option R
for such reveal slide decks.
Two dimensional slides may be a bit new to some people, so I like to
give viewers an option, in tiny font, to view the slide-deck
continuously and remind them that ?
provides useful shortcuts.
(setq org-reveal-title-slide "<h1>%t</h1> <h3>%a</h3> <font size=\"1\"> <a href=\"?print-pdf&showNotes=true\"> ⟪ Flattened View ; Press <code>?</code> for Help ⟫ </a> </font>")
One should remove the &showNotes=true
if they do not want to include
speaker notes in the flattened view.
Within the flatenned view, one may wish to CTRL/CMD+P
then save the
resulting PDF locally.
Org-mode ⇐ HTML
The following let’s us copy htlm into org format using eww, Emacs’ built-in web browser.
;; See: https://emacs.stackexchange.com/questions/7171/paste-html-into-org-mode (use-package org-eww :quelpa (org-eww :fetcher git :url "https://github.com/Fuco1/org-mode.git"))
It does not work as I’d like, but may prove useful to have around.
- Possibly useful: Open a webpage with
M-x eww
then toggleM-x read-only-mode
to edit the text, say for notes or deletions, as you read! No need to copy-paste.
Life within Org-mode
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!
Using Org-Mode as a Day Planner
⟪ This section is based on a dated, yet delightful, tutorial of the same title by John Wiegley. ⟫
We want a day-planner with the following use:
- “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.
Capturing ideas & notes without interrupting the current workflow
Capture lets me quickly make notes & capture ideas, with associated reference material, without any interruption to the current work flow. Without losing focus on what you’re doing, quickly jot down a note of something important that just came up.
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.
⇒ Org capture actually lets us add any type of entry, ‘programmable template’, to any type of file! ⇐
( For now, I’m only using it to add entries to my tasks lists. )
By default we only get a ‘tasks’ form of capture, let’s add some more.
(cl-defun my/make/org-capture-template (shortcut heading &optional (no-todo nil) (description heading) (scheduled nil)) "Quickly produce an org-capture-template. After adding the result of this function to ‘org-capture-templates’, we will be able perform a capture with “C-c c ‘shortcut’” which will have description ‘description’. It will be added to the tasks file under heading ‘heading’. ‘no-todo’ omits the ‘TODO’ tag from the resulting item; e.g., when it's merely an interesting note that needn't be acted upon. Default for ‘description’ is ‘heading’. Default for ‘no-todo’ is ‘nil’. Scheduled items appear in the agenda; true by default. The target is ‘file+headline’ and the type is ‘entry’; to see other possibilities invoke: C-h o RET org-capture-templates. The “%?” indicates the location of the Cursor, in the template, when forming the entry. " `(,shortcut ,description entry (file+headline org-default-notes-file ,heading) ,(concat "*" (unless no-todo " TODO") " %?\n" (when nil ;; this turned out to be a teribble idea. ":PROPERTIES:\n:" (if scheduled "SCHEDULED: %^{Any time ≈ no time! Please schedule this task!}t" "CREATED: %U") "\n:END:") "\n\n ") :empty-lines 1 :time-prompt t)) (setq org-capture-templates (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))) ;; Update: Let's schedule tasks during the GTD processing phase. ;; ;; For now, let's automatically schedule items a week in advance. ;; TODO: FIXME: This overwrites any scheduling I may have performed. ;; (defun my/org-capture-schedule () ;; (org-schedule nil "+7d")) ;; ;; (add-hook 'org-capture-before-finalize-hook 'my/org-capture-schedule)
For now I capture everything into a single file. One would ideally keep separate client, project, information in its own org file.
Rather than adding notes to particular Org headings in my todo.org
file, I could
defer such a choice by having only one template and have C-c a
automatically use
it. Then I could ‘refile’ tasks to their appropriate parent headings with w
.
This allows us to seperate the concerns of capturing ideas from doing any form
of processing. Something to consider.
- Again: 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…
Step 1: When new tasks come up
Isn’t it great that we can squirrel away info into some default location then immediately return to what we were doing before —with speed & minimal distraction! ‿ Indeed, if our system for task management were slow then we may not produce tasks and so forget them altogether! щ(゜ロ゜щ)
- Entering tasks is a desirably impulsive act; do not make any further
scheduling considerations.
The next step, the review stage occurring at the end or the start of the workday, is for processing.
The reason for this is that entering new tasks should be impulsive, not reasoned. Your reasoning skills are required for the task at hand, not every new tidbit. You may even find that during the few hours that transpire between creating a task and categorizing it, you’ve either already done it or discovered it doesn’t need to be done at all! —John Wiegley
When my computer isn’t handy, I’ll make a note on my phone then transfer it later.
Step 2: Filing your tasks
At a later time, a time of reflection, we go to our tasks list and actually
schedule time to get them done by C-c C-s
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)
Step 3: Quickly review the upcoming week
The next day we begin our work, we press C-c a a
to see the
scheduled tasks for this week —~C-c C-s~ to re-schedule the
task under the cursor and r
to refresh the agenda.
(define-key global-map "\C-ca" 'org-agenda)
- Show the next 𝓃 days schedule ⇐
C-u 𝓃 C-c a a
.
The next section, Super Agenda, will discuss acting on entries in the agenda buffer.
Step 4: Getting ready for the day
After having seen our tasks for the week, we press d
to enter daily view
for the current day. Now we decide whether the items for today are
A
: of high urgency & important; B
: of moderate urgency & importance; or
C
: Pretty much optional, or very quick or fun to do.
A
tasks should be both important and urgently done on the day they were scheduled.- Such tasks should be relatively rare!
- If you have too many, you’re anxious about priorities and rendering priorities useless.
C
tasks can always be scheduled for another day without much worry.- Act! If the thought of rescheduling causes you to worry, upgrade it to a
B
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.
Step 5: Doing the work
Since A
tasks are the important and urgent ones, if you do all of the A
tasks and
nothing else today, no one would suffer. It’s a good day (─‿‿─).
There should be no scheduling nor prioritising at this stage. You should not be touching your tasks file until your next review session: Either at the end of the day or the start of the next.
- Leverage priorities! E.g., When a full day has several
C
tasks, reschedule them for later in the week without a second thought.- You’ve already provided consideration when assigning priorities.
Step 6: Moving a task toward completion
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.
Step 7: Archiving Tasks
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. ;; ➩ Show my agenda upon Emacs startup. (org-agenda "a" "a") ;; Need this to have “org-agenda-custom-commands” defined.
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=.
Tag! You’re it!
Even when items are categorised under their own parent headings, they may be
related in some way and that can made explicit by adding a :tag:
to their
headings; e.g., two entries both have the :jasim:@work:
tags, then looking for
the :@work:
tag shows me all entries that are tagged as “at work”.
Tags provide a cross-section of one’s entries. |
Tags let us find related stuff quickly, even though they’re differently categorised.
After calling ~org-agenda~, we may select ~m~ to match for tags, or use ~org-tags-view~ to search for tags.
What to tag? Common tags are :@laptop:, :@work:, :@home:
to identify the
location where tasks take place —Use: When I’m at a particular place, I need
only consider tasks that apply to that place ;-) Other tags I use are :𝑭𝑳:
to
identify remarks or email or request from person 𝑭irstname 𝑳astname; or
something that might be interesting to that person. I also use ~:video:, ,
:project_name:
to identify notes that may be of interest to a particular
project, but are more appropriately categorised elsewhere —e.g., when learning
about an Emacs feature, I may tag my notes with another project’s name to
consider whether that feature could be useful there.
How to tag?
You can just add a :tag₁:⋯:tagₙ:
after a heading. If you press space, before the
tags, then they are automatically indented flushright to column 77; postive
numbers do not flushright but use exact column number.
(setq org-tags-column -77) ;; the default
Use C-c C-q
, or org-set-tags-command
, on a heading or just the speed key :
on
the asterisks of a heading to set the tags of an item —as usual, with Helm we
obtain a window of all existing tags to select from. Unfortunatley, this only
supports having one tag; for more, you can add them in manually or …
(use-package helm-org) ;; Helm for org headlines and keywords completion. (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command . helm-org-completing-read-tags)) ;; Also provides: helm-org-capture-templates
Now :
or C-c C-q
will show existing tags for the current heading, press TAB
to
obtain a list of all exisiting tags, press C-SPC
to select the desired tags,
then TAB
or RET
to confirm the resulting tag list, and RET
to finish or TAB
to
select more tags.
Let’s render tags by Unicode symbols.
(use-package org-pretty-tags :diminish org-pretty-tags-mode :demand t :config (setq org-pretty-tags-surrogate-strings '(("Neato" . "💡") ("Blog" . "✍") ("Audio" . "♬") ("Video" . "📺") ("Book" . "📚") ("Running" . "🏃") ("Question" . "❓") ("Wife" . "💕") ("Text" . "💬") ; 📨 📧 ("Friends" . "👪") ("Self" . "🍂") ("Finances" . "💰") ("Car" . "🚗") ; 🚙 🚗 🚘 ("Urgent" . "🔥"))) ;; 📥 📤 📬 (org-pretty-tags-global-mode 1))
Pretty Prioritisation Markers
Let’s set four priority levels and their colours: The more intense colours are for more urgent tasks.
(setq org-lowest-priority ?D) ;; Now org-speed-eky ‘,’ gives 4 options (setq org-priority-faces '((?A :foreground "red" :weight bold) (?B . "orange") (?C . "yellow") (?D . "green")))
- ~C-c ,~ anywhere to set the priority of the current heading.
- We may press
A-D
orSPC
to an remove existing priority.
- We may press
Priority markers are of the form [#𝒳]
, the fancy priorities package visually
renders them as words or icons.
(use-package org-fancy-priorities :diminish t :hook (org-mode . org-fancy-priorities-mode) :custom (org-fancy-priorities-list '("HIGH" "MID" "LOW" "OPTIONAL")))
Super Agenda
Org agenda is an interactive tool for generating summary reports from Org data —e.g., commonly, the weekly task list is generated from todo tasks.
The agenda dispatch menu, C-c a
, has options for displaying tasks —e.g., C-c a
m
generates a list of entries having the same tags. new ways to view tasks by
altering the org-agenda-custom-commands
variable —e.g., above we added two,
one for completed tasks and one for unscheduled tasks.
Let’s setup the basics of our agenda.
;; List of all the files & directories where todo items can be found. Only one ;; for now: My default notes file. (setq org-agenda-files (list org-default-notes-file)) ;; Display tags really close to their tasks. (setq org-agenda-tags-column -10) ;; How many days ahead the default agenda view should look (setq org-agenda-span 'day) ;; May be any number; the larger the slower it takes to generate the view. ;; One day is thus the fastest ^_^ ;; How many days early a deadline item will begin showing up in your agenda list. (setq org-deadline-warning-days 14) ;; In the agenda view, days that have no associated tasks will still have a line showing the date. (setq org-agenda-show-all-dates t) ;; Scheduled items marked as complete will not show up in your agenda view. (setq org-agenda-skip-scheduled-if-done t) (setq org-agenda-skip-deadline-if-done t)
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 $𝓃$ means do the action
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 :hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB. :bind (:map org-super-agenda-header-map ("<tab>" . origami-toggle-node)) :config (org-super-agenda-mode) (setq org-super-agenda-groups '((:name "Important" :priority "A") (:name "Personal" :habit t) ;; For everything else, nicely display their heading hierarchy list. (:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path))))))) ;; MA: No noticable effect when using org-super-agenda :/ ;; ;; Leave new line at the end of an entry. ;; (setq org-blank-before-new-entry '((heading . t) (plain-list-item . t)))
- 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. ⟩
Journaling
Thus far I’ve made it easy to quickly capture ideas and tasks, not so much on the analysis phase:
- What was accomplished today?
- What are some notably bad habits? Good habits?
- What are some future steps?
Rather than overloading the capture mechanism for such thoughts, let’s employ
org-journal
—journal entries are stored in files such as journal/20190407
,
where the file name is simply the date, or only one file per year as I’ve set it
up below. Each entry is the week day, along with the date, then each child tree
is an actual entry with a personal title preceded by the time the entry was
made. Unlike capture and its agenda support, journal ensures entries are
maintained in chronological order with calendar support.
Since org files are plain text files, an entry can be written anywhere and later ported to the journal. Or, written directly in the journal file if we add the necessary Org-header: Asterisks and time.
The separation of concerns is to emphasise the capture stage as being quick and relatively mindless, whereas the journaling stage as being mindful. Even though we may utilise capture to provide quick support for including journal entries, I have set my journal to be on a yearly basis —one file per year— since I want to be able to look at previous entries when making the current entry; after all, it’s hard to compare and contrast easily unless there’s multiple entries opened already.
As such, ideally at the end of the day, I can review what has happened, and what has not, and why this is the case, and what I intend to do about it, and what problems were encountered and how they were solved —in case the problem is encountered again in the future. *Consequently, if I encounter previously confronted situations, problems,* all I have to do is reread my journal to get an idea of how to progress. Read more about the importance of reviewing your day on a daily basis.
Moreover, by journaling with Org on a daily basis, it can be relatively easy to produce a report on what has been happening recently, at work for example. I’d like to have multiple journals, for work and for personal life, as such I will utilise a prefix argument to obtain my work specific entries.
The Setup
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"))
Super Terse Tutorial
Bindings available in org-journal-mode
, when journaling:
C-c C-j
: Insert a new entry into the current journal file.- Note that keys for
org-journal-new-entry
shadow those 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.
Guided Journaling
Sometimes it can be tough to journal, but filling in a template can be a way to
get started. Later on, we will setup 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 (•̀ᴗ•́)و
Workflow States
Here are some of my common workflow states, —the ‘X/Y’ indicates to do action ‘X’ when entering a state and ‘Y’ when leaving it, with ‘!’ denoting a timestamp should be generated and ‘@’ denoting a user note should be made.
(setq org-todo-keywords '((sequence "TODO(t)" "STARTED(s@/!)" "|" "DONE(d/!)") (sequence "WAITING(w@/!)" "ON_HOLD(h@/!)" "|" "CANCELLED(c@/!)"))) ;; Since DONE is a terminal state, it has no exit-action. ;; Let's explicitly indicate time should be noted. (setq org-log-done 'time)
The @
brings up a pop-up to make a local note about why the state changed.
Super cool stuff!
In particular, we transition from TODO
to STARTED
once 15 minutes, or a
reasonable amount, of work has transpired. Since all but one state are marked
for logging, we could use the lognotestate
logging facility of org-mode, which
prompts for a note every time a task’s state is changed.
Entering a comment about what I’ve done, even if it’s very little, feels like
I’m getting something done. It’s an explicit marker of progress and motivates me
to want to change my task’s states more often until I see it marked DONE
.
Here’s how they are coloured,
(setq org-todo-keyword-faces '(("TODO" :foreground "red" :weight bold) ("STARTED" :foreground "blue" :weight bold) ("DONE" :foreground "forest green" :weight bold) ("WAITING" :foreground "orange" :weight bold) ("ON_HOLD" :foreground "magenta" :weight bold) ("CANCELLED" :foreground "forest green" :weight bold)))
Now we press C-c C-t
then the letter shortcut to actually make the state of an org heading.
(setq org-use-fast-todo-selection t)
We can also change through states using Shift- left, or right.
Let’s draw a state diagram to show what such a workflow looks like.
PlantUML supports drawing diagrams in a tremendously simple format —it even supports Graphviz/DOT directly and many other formats. Super simple setup instructions can be found here; below are a bit more involved instructions. Read the manual here.
;; Install the tool ; (async-shell-command "brew 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 "/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.
Clocking Work Time
Let’s keep track of the time we spend working on tasks that we may have captured for ourselves the previous day. Such statistics provides a good idea of how long it actually takes me to accomplish a certain task in the future and it lets me know where my time has gone.
Clock inon a heading with I
, or in the subtree with C-c C-x C-i
.
Clock outof a heading with O
, or in the subtree with C-c C-x C-o
.
Clock reportSee 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)
Finding tasks to clock in
Use one of the following options, with the top-most being the first to be tried.
- From anywhere,
C-u C-c C-x C-i
yields a pop-up for recently clocked in tasks. - Pick something off today’s agenda scheduled items.
- Pick a
Started
task from the agenda view, work on this unfinished task. - Pick something from the
TODO
tasks list in the agenda view.
C-c C-x C-d
also provides a quick summary of clocked time for the current org file.
Estimates versus actual time
Before clocking into a task, add to the properties drawer :Effort: 1:25
or C-c
C-x C-e
, for a task that you estimate will take an hour and twenty-five minutes,
for example. Now the modeline will mention the time elapsed alongside the task
name. Woah!
(push '("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") org-global-properties)
Use speed keys
e/E
to insert an effort estimate, with the above being provided options, or to increment the current effort to the next one in the above list.
This is also useful when you simply want to put a time limit on a task that wont be completed anytime soon, say writing a thesis or a long article, but you still want to work on it for an hour a day and be warned when you exceed such a time constraint.
When you’ve gone above your estimate time, the modeline colours it red.
Habit Formation
The reason to use habits is that they come with a graph indicating consistency by colour, and the goal of the game is to have the longest possible chain —no red days!
A ‘habit’ is a usual (recurring) todo task marked as a habit:
Use C-c C-x p
to set the STYLE
property to habit
on a task to set it as a habit.
;; Show habits for every day in the agenda. (setq org-habit-show-habits t) (setq org-habit-show-habits-only-for-today nil) ;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading. (setq org-habit-graph-column 90) ;; In order to see the habit graphs, which I've placed rightwards, let's ;; always open org-agenda in ‘full screen’. ;; (setq org-agenda-window-setup 'only-window)
inch by inch anything’s a cinch! |
!
means today and ⋆
means a task has been done on that day;
intuitively green means you’re on track, yellow is warning sign of overdue,
red is overdue, and blue is an acceptable break day.
Here’s an example habit from the Org-mode manual, where .+𝒳d/𝒴d
reads
perform the habit once every 𝒳 days, but never let me go 𝒴 days without doing it.
** TODO Shave SCHEDULED: <2020-01-08 Wed .+2d/4d> :PROPERTIES: :STYLE: habit :END:
Shave every 2 days, but we can take a 3-day break; however, on the 4th day, gotta shave!
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.
Programming
Herein we configure utilites for version control, function and variable lookup, and template expansion for inescapably repetitive scenarios.
Which function are we writing?
In the modeline, show the name of the function we’re currently writing.
(add-hook 'prog-mode-hook #'which-function-mode) (add-hook 'org-mode-hook #'which-function-mode)
In Org-mode, this places the current heading in the modeline.
In Lisp mode, ensure we always have matching parens.
(add-hook 'emacs-lisp-mode-hook #'check-parens)
Highlight defined Lisp symbols
Usually Emacs only highlights macro names, the following incantation makes it highlight all defined names —as long as we’re in Lisp mode, whence in org-src blocks we use ~C-c ‘~.
;; Emacs Lisp specific (use-package highlight-defined :hook (emacs-lisp-mode . highlight-defined-mode))
Super helpful in making my Emacs configuration: If a name is not highlighted, then I’ve misspelled it or it doesn’t exist!
Eldoc for Lisp and Haskell
In emacs-lisp-mode
we can enable eldoc-mode
—“Elisp Live Documentation”— to
display information about a function or a variable in the echo area. Likewise
for Haskell.
(use-package eldoc :diminish eldoc-mode :hook (emacs-lisp-mode . turn-on-eldoc-mode) (lisp-interaction-mode . turn-on-eldoc-mode) (haskell-mode . turn-on-haskell-doc-mode) (haskell-mode . turn-on-haskell-indent))
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.
Jumping to definitions & references
Out-of-the-box Emacs has ‘xref’ utilities M-.
and C-u M-.
to Find Identifier
References; however, tags to source definitions need to be generated using the
etags
program. Nonetheless, the xref utilites are impressive and some just work:
For example, M-?
cleverly finds all references for an identifier in ‘near by’
files; whereas C-u M-. RET my/.*
, for example, uses the given regular expression
to list all identifiers with prefix my/
, thereby listing my personally defined
names ^_^
C-M-. 𝓇𝓮ℊ𝓮𝓍 | Find all identifiers whose name matches the given pattern |
Let’s get dumb-jump, where the ‘dumb’ is possibly due to the fact that it works by brute-force regular-expression lookup of pre-defined ‘definitional template’ rules. It “just works” ^_^
(use-package dumb-jump :bind (("M-g q" . dumb-jump-quick-look) ;; Show me in a tooltip. ("M-g ." . dumb-jump-go-other-window) ("M-g b" . dumb-jump-back) ("M-g p" . dumb-jump-go-prompt) ("M-g a" . xref-find-apropos)) ;; aka C-M-. :config ;; If source file is visible, just shift focus to it. (setq dumb-jump-use-visible-window t))
In Lisp, for binding macros, it lists all possible mentions of the bound
variable —the first is likely what is desired. Alternatively, one could just
add the necessary rule to the variable dumb-jump-find-rules
. Otherwise, it
works fine even for locally bound definitions. It works depending on the
extension of a file.
Version Control with SVN —Using Magit!
Let’s use git as an interface to subversion repositories so that we can continue
to use magit
as our version control interface. The utility to do so is called
git svn
—note git 𝒳
on a MacOS is the same as git-𝒳
on other systems.
(use-package magit-svn :hook (magit-mode . magit-svn-mode))
Here’s an example. The following command checksout an SVN repo; afterwhich we may open a file
there and do M-x magit-status
to get the expected porcelain git interface ^_^
(async-shell-command "mkdir ~/2fa3; git svn clone --username alhassm https://websvn.mcmaster.ca/csse2fa3/2019-2020_Term2 ~/2fa3/")
In the magit buffer, we may now use the N
key which wraps the git svn
subcommands fetch, rebase, dcommit, branch, tag
. For example:
- Make changes to a file.
- ‘Stage’ them with
s
and ‘commit’ them withc
. - ‘Push’ changes with
N c
.
We get to pretend we’re using git
even though the underlying mechanism is svn
!
For move on git svn
, see A simple guide to git-svn or Effectively using Git
with Subversion.
⟨ If I need to work with svn repos often enough, I’d extend my maybe-clone
utility above to account for them. ⟩
What’s changed & who’s to blame?
Let’s have, in a fringe, an indicator for altered regions in a version controlled file. The symbols “+, =” appear in a fringe by default for alterations —we may change these if we like.
;; Hunk navigation and commiting. (use-package git-gutter :diminish :config (global-git-gutter-mode)) ;; Diff updates happen in real time according when user is idle.
Let’s set a hydra so we can press C-x v n n p n
to move the next two
altered hunks, move back one, then move to the next. This saves me having
to supply the prefix C-x v
each time I navigate among my alterations.
At any point we may also press u 𝕩
to denote C-u ⟪prefix⟫ 𝕩
.
(defhydra hydra-version-control (global-map "C-x v") "Version control" ;; Syntax: (extension method description) ("n" git-gutter:next-hunk "Next hunk") ("p" git-gutter:previous-hunk "Previous hunk") ("d" git-gutter:popup-hunk "Show hunk diff") ("r" git-gutter:revert-hunk "Revert hunk\n") ("c" git-gutter:stage-hunk "Stage hunk") ("s" git-gutter:statistic "How many added & deleted lines"))
Commiting with C-x v c
let’s us use C-c C-k
to cancel and C-c C-c
to
submit the given message; C-c C-a
to amend the previous commit.
Alternatively, we may use diff-hl:
;; Colour fringe to indicate alterations. ;; (use-package diff-hl) ;; (global-diff-hl-mode)
A few more helpful version control features:
;; Popup for who's to blame for alterations. (use-package git-messenger :custom ;; Always show who authored the commit and when. (git-messenger:show-detail t) ;; Message menu let's us use magit diff to see the commit change. (git-messenger:use-magit-popup t)) ;; View current file in browser on github. ;; More generic is “browse-at-remote”. (use-package github-browse-file :defer t) ;; Add these to the version control hydra. ;; (defhydra hydra-version-control (global-map "C-x v") ("b" git-messenger:popup-message "Who's to blame?") ;; C-u C-x b ╱ u b ∷ Also show who authored the change and when. ("g" github-browse-file-blame "Show file in browser in github") ("s" magit-status "Git status of current buffer"))
Perhaps C-x v b
will motivate smaller, frequent, commits.
Obtaining URL links to the current location of a file —URLs are added to the kill ring. Usefully, if git-timemachine-mode is active, the generated link points to the version of the file being visited.
(use-package git-link :defer t) (defhydra hydra-version-control (global-map "C-x v") ("l" git-link "Git URL for current location"))
Read here for more about version control in general.
Highlighting TODO-s & Showing them in Magit
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 ;; Adding new keywords (loop for kw in '("TEST" "MA" "WK" "JC") do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))) (global-hl-todo-mode)) ;; Enable it everywhere.
Lest these get buried in mountains of text, let’s have them become
mentioned in a magit status buffer —which uses the keywords from hl-todo
.
;; MA: The todo keywords work in code too! (use-package magit-todos :after magit :after hl-todo :hook (org-mode . magit-todos-mode) :config ;; For some reason cannot use :custom with this package. (custom-set-variables '(magit-todos-keywords (list "TODO" "FIXME" "MA" "WK" "JC"))) ;; Ignore TODOs mentioned in exported HTML files; they're duplicated from org src. (setq magit-todos-exclude-globs '("*.html")) (magit-todos-mode))
Note that such TODO keywords are not propagated from sections that are COMMENT-ed out in org-mode.
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."))
Being Generous with Whitespace
The following minor mode automatically adds spacing around operators.
(use-package electric-operator :diminish :hook (c-mode . electric-operator-mode))
I dislike it when users write x=y+1
—whitespace is free and helpful. ⟨ Also,
languages with arbitrary identifiers, like Lisp and Agda, would accept x=y+1
as
an identifier, not an expression! ⟩
On the fly syntax checking
Flycheck is a on-the-fly syntax checker that relies on external programs to check buffers; which must be installed separately.
- E.g., ghc is required for Haskell; whereas Emacs Lisp is checked by Emacs’
own byte compiler,
emacs-lisp
. - Sometimes more than one checking tool applies, use
C-c ! s
to select a different checker. C-c ! n,p,l
takes you to the ‘n’ext or ‘p’revious error, or ‘l’ist all errors in another buffer.C-c ! c
to explicitly recheck the buffer.
(use-package flycheck :diminish :init (global-flycheck-mode) :config ;; There may be multiple tools; I have GHC not Stack, so let's avoid that. (setq-default flycheck-disabled-checkers '(haskell-stack-ghc emacs-lisp-checkdoc)) :custom (flycheck-display-errors-delay .3))
In an org-src block, we press ~C-c ‘~ to get into the language’s mode where flycheck will provide warnings.
module Main where main :: IO () main = putStrLn $ "nice" ++ f 0 f :: Int -> String f x = x -- show x -- type error
In-general, flycheck is intended for self-contained raw code —not for source
blocks in Org-mode. Whence, the above example is a complete Haskell program,
with a named module and main
method.
I think the built-in flymake syntax checker is better for Emacs Lisp, so let’s use that for ELisp.
(use-package flymake :hook ((emacs-lisp-mode . (lambda () (flycheck-mode -1))) (emacs-lisp-mode . flymake-mode)) :bind (:map flymake-mode-map ("C-c ! n" . flymake-goto-next-error) ("C-c ! p" . flymake-goto-prev-error)))
Try it out:
(setq 1 2) ;; Error: ‘1’ is not a variable.
Coding with a Fruit Salad: Semantic Highlighting
What should be highlighted when we write code? Static keywords with fixed uses, or dynamic user-defined names?
- Syntax highlighting ⇨ Specific words are highlighted in strong colours so that
the structure can be easily gleaned.
- Generally this only includes a language’s keywords, such as
if, loop, begin, end, cond
. - User defined names generally share one colour; usually black.
- Hence, an
if
block may be seen as one coloured keyword followed by a blob of black text.
Obvious keywords are highlighted while the rest remains in black!
- 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)) ;; Sometimes just invoke: M-x color-identifiers:refresh
When writing a new name, after about ~5 seconds it obtains a colour which is then
propagated immediately to any new occurrences. This timeout before recolouring
is to avoid any lag from multithreading and can be changed by altering the following
line (#64) in the source file, changing the 5
to a smaller number.
(run-with-idle-timer 5 t 'color-identifiers:refresh)
Here are further reads:
Text Folding with Origami-mode
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 ;; In Lisp languages, by default only function definitions are folded. :hook ((agda2-mode lisp-mode c-mode) . origami-mode) :config ;; With basic support for one of my languages. (push '(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"))
Jump between windows using Cmd+Arrow & between recent buffers with Meta-Tab
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.
Snippets —Template Expansion
It is common that there is a sequence of text that we tend to repeat
often, possibly with a name or some other parameter altered.
Such a ‘snippet’ could be written once then provided by a simple
Lisp insert command with the parameters being queried. Luckily, others
have written such pleasant utilities.
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.
Org-mode Templates —A reason I “generate” templates ;)
This produces a pop-up list of org-mode block types, if src
is selected, then a
list of my commonly used languages pops-up. Alternatively, ignore the pop-up
menu and write any block or language name.
** begin: produce an org-mode block #+begin_${1:environment$(let* ((block '("src" "example" "quote" "verse" "center" "latex" "html" "ascii")) (langs '("c" "emacs-lisp" "lisp" "latex" "python" "sh" "haskell" "plantuml" "prolog")) (type (yas-choose-value block))) (concat type (when (equal type "src") (concat " " (yas-choose-value langs)))))} $0 #+end_${1:$(car (split-string yas-text))}
In this case, yas-text
is equivalent to (yas-field-value 1)
;
it generally refers to the value of the field being mirrored with ${n: ⋯yas-text⋯}
.
However, going through pop-ups takes precious time —besides being slightly annyoing. Let’s introduce a template for my most utilised kind of language blocks.
** s_org: src block for org #+begin_src org $0 #+end_src
However, doing this for each language I want is a waste of time and textual
space. Why? The purpose of templates is to reduce repetition, yet the above
block would be repeated with only 3 parts ‘unknown’: The expansion keyword, the
description, and the org-mode source block name. Whence, the template text is
generated by the following basic loop —whose source block is named
my-org-lang-templates
.
;; We make an org BLOCK snippet template for each LANG the user has declared. ;; (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.
Elisp Templates
The following snippets were rather useful as I began learning Lisp to construct
my editor of choice —I love Emacs so much. Admittedly, I still need the first
one below and usually beat around the bush by using (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₂}) )
Equational Templates
To show ℒ = ℛ
, one starts at the complicated side, say ℒ, then, with the aim of
simplification, tries to end at the simpler side, 𝓡. Along the way, one
justifies each step of the calculation. This approach is popular in the proof
assistant Agda; Examples. Read more about informal calculational proofs.
** fun: Function declaration with type signature ${1:fun-name} : ${2:arguments} $1 ${3:args} = ?$0 ** eqn_begin: Start a ≡-Reasoning block in Agda begin ${1:complicated-side} $0≡⟨ ${3:reason-for-the-equality} ⟩ ${2:simpler-side} ∎ ** eqn_step: Insert a step in a ≡-Reasoning block in Agda ≡⟨ ${2:reason-for-the-equality} ⟩ ${1:new-expression} $0
One expands eqn_begin
, tabs to fill in the three main locations, then
immediately types eqn_step
to produce a new step in a calculational proof.
my_⋯
Templates to obtain User Information
Let’s add templates for links to common user information ^_^
** my_name: User's name `user-full-name` ** my_email: User's email address `user-mail-address` ** my_github: User's Github repoistory link https://github.com/alhassy/ ** my_emacsdrepo: User's version controlled Emacs init file https://github.com/alhassy/emacs.d ** my_blog: User's blog website https://alhassy.github.io/ ** my_webpage: User's organisation website http://www.cas.mcmaster.ca/~alhassm/ ** my_twitter: User's Twitter profile https://twitter.com/musa314 ** my_masters_thesis A Mechanisation of Internal Galois Connections In Order Theory Formalised Without Meets https://macsphere.mcmaster.ca/bitstream/11375/17276/2/thesis.pdf
It may be useful to also have Org-link variants of these …
Templates from other places in my init
In this setup, I have some templates appear elsewhere, tagged with :noweb-ref
templates-from-other-places-in-my-init
. They are presented in natural positions,
but can only occur to the machine after template expansion is setup. Using
org-mode, we are able to present code in any order and tangle it to the order
the compilers need it to be!
Let’s activate all such templates, now after template expansion has been setup.
<<templates-from-other-places-in-my-init>>
Note: Since I’ve insisted that Org blocks are space sensative, any whitespace
before the <<⋯>>
will propogate to the resulting extracted code.
Re-Enabling Templates
After our yankpad templates are generated, we need to load it.
;; After init hook; see above near use-package install. (yankpad-reload)
Helpful Utilities & Shortcuts
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)
Documentation Pop-Ups
Let documentation pop-up when we pause on a completion. This is very useful when editing in a particular coding language, say via ~C-c ‘~ for org-src blocks.
(use-package company-quickhelp :config (setq company-quickhelp-delay 0.1) (company-quickhelp-mode))
Emacs keybindings for my browser
⟨ 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 ^_^
Using Emacs in any text area on my OS
⟨ 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")))
Reload buffer with f5
I do this so often it’s not even funny.
(global-set-key [f5] '(lambda () (interactive) (revert-buffer nil t nil)))
In Mac OS, one uses Cmd-r
to reload a page and Emacs binds buffer reversion to Cmd-u
–in Emacs, Mac’s Cmd
is referred to as the ‘super key’ and denoted s
.
Moreover, since I use Org-mode to generate code blocks and occasionally inspect them, it would be nice if they automatically reverted when they were regenerated –Emacs should also prompt me if I make any changes!
;; Auto update buffers that change on disk. ;; Will be prompted if there are changes that could be lost. (global-auto-revert-mode 1) ;; Don't show me the “ARev” marker in the mode line (diminish 'auto-revert-mode)
Kill to start of line
Dual to C-k
,
;; M-k kills to the left (global-set-key "\M-k" '(lambda () (interactive) (kill-line 0)) )
Killing buffers & windows: C-x k
has a family
Let’s extend the standard C-x k
with prefix support, so that we can invoke
variations: Kill this buffer, kill other buffer, or kill all other buffers.
By default C-x k
prompts to select which buffer should be selected. I almost
always want to kill the current buffer, so let’s not waste time making such a
tedious decision. Moreover, if I’ve killed a buffer, I usually also don’t want
the residual window, so let’s get rid of it.
(global-set-key (kbd "C-x k") (lambda (&optional prefix) "C-x k ⇒ Kill current buffer & window C-u C-x k ⇒ Kill OTHER window and its buffer C-u C-u C-x C-k ⇒ Kill all other buffers and windows Prompt only if there are unsaved changes." (interactive "P") (pcase (or (car prefix) 0) ;; C-x k ⇒ Kill current buffer & window (0 (kill-this-buffer) (unless (one-window-p) (delete-window))) ;; C-u C-x k ⇒ Kill OTHER window and its buffer (4 (other-window 1) (kill-this-buffer) (unless (one-window-p) (delete-window))) ;; C-u C-u C-x C-k ⇒ Kill all other buffers and windows (16 (mapc 'kill-buffer (delq (current-buffer) (buffer-list))) (delete-other-windows)))))
The incantation C-u C-x k
will reduce the noise of all the documentation buffers
I tend to consult.
Switching from 2 horizontal windows to 2 vertical windows
I often find myself switching from a horizontal view of two windows in Emacs to a
vertical view. This requires a variation of C-x 1 RET C-x 3 RET C-x o C-x b RET
.
Instead I now only need to type C-|
to make this switch.
(defun my/ensure-two-vertical-windows () "I used this method often when programming in Coq. When there are two vertical windows, this method ensures the left-most window contains the buffer with the cursour in it." (interactive) (let ((otherBuffer (buffer-name))) (other-window 1) ;; C-x 0 (delete-window) ;; C-x 0 (split-window-right) ;; C-x 3 (other-window 1) ;; C-x 0 (switch-to-buffer otherBuffer) ;; C-x b RET (other-window 1))) (global-set-key (kbd "C-|") 'my/ensure-two-vertical-windows)
Obtaining Values of #+KEYWORD
Annotations
Org-mode settings are, for the most part, in the form #+KEYWORD: VALUE
. Of notable interest
are the TITLE
and NAME
keywords. We use the following org-keywords
function to obtain
the values of arbitrary #+THIS : THAT
pairs, which may not necessarily be supported by native
Org-mode –we do so for the case, for example, of the CATEGORIES
and IMAGE
tags associated with an article.
;; Src: http://kitchingroup.cheme.cmu.edu/blog/2013/05/05/Getting-keyword-options-in-org-files/ (defun org-keywords () "Parse the buffer and return a cons list of (property . value) from lines like: #+PROPERTY: value" (org-element-map (org-element-parse-buffer 'element) 'keyword (lambda (keyword) (cons (org-element-property :key keyword) (org-element-property :value keyword))))) (defun org-keyword (KEYWORD) "Get the value of a KEYWORD in the form of #+KEYWORD: value" (cdr (assoc KEYWORD (org-keywords))))
Note that capitalisation in a ”#+KeyWord” is irrelevant.
See here on how to see the abstract syntax tree of an org file and how to manipulate it.
Publishing articles to my personal blog
I try to blog occasionally, so here’s a helpful function to quickly publish the current article to my blog.
(define-key global-map "\C-cb" 'my/publish-to-blog) (cl-defun my/publish-to-blog (&optional (draft nil) (local nil)) " Using ‘AlBasmala’ setup to publish current article to my blog. Details of AlBasmala can be found here: https://alhassy.github.io/AlBasmala/ Locally: ~/alhassy.github.io/content/AlBasmala.org A ‘draft’ will be produced in about ~7 seconds, but does not re-produce a PDF and the article has a draft marker near the top. Otherwise, it will generally take ~30 seconds due to PDF production, which is normal. The default is not a draft and it takes ~20 seconds for the live github.io page to update. The ‘local’ optiona indicates whether the resulting article should be viewed using the local server or the live webpage. Live page is default. When ‘draft’ and ‘local’ are both set, the resulting page may momentarily show a page-not-found error, simply refresh. " (load-file "~/alhassy.github.io/content/AlBasmala.el") ;; --MOVE ME TO ALBASMALA-- ;; Sometimes the file I'm working with is not a .org file, so: (setq file.org (buffer-name)) (preview-article :draft draft) (unless draft (publish)) (let ((server (if local "http://localhost:4000/" "https://alhassy.github.io/"))) (async-shell-command (concat "open " server NAME "/") "*blog-post-in-browser*")) )
Conclusion —Why Configuration Files Should be Literate
A configuration file sets up various features for a tool —and serves as an essential learning point. In order to remember them, what they do, and possibly where you learned about them —which may include additional resources— it is pertinent to document such facts. Benefits of documentating features include:
- A list of the features with human readable names! —In case you forget what you invested time on!
- Personal documentation! —Reduce wasting time Googling things that you knew in the past!
- Convincing Need
- Making notes with decriptive text, as suggested below,
will make it clear whether you actually need the feature
or “just threw it becuase it looks cool” —which leads to ‘init bankruptcy’.
Moreover, actually documenting a feature may make it more to recall that you have the feature and have notes for it.
- 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 ^_^
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK