#
badge:A_Life|Configuring_Emacs|success|https://github.com/alhassy/emacs.d|gnu-emacs
badge:A_Life_Configuring|Emacs|success|https://alhassy.github.io/emacs.d/|gnu
Abstract
Hello! Herein I document the configurations I utilise with Emacs.
gnu 3 license badge tweet:https://alhassy.com/emacs.d/
badge:author|musa_al-hassy|purple|https://alhassy.github.io/|nintendo-3ds badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee
This article is about how I like to do things —/I’m not insisting others should/ do things this way.
The following creates the “Github Actions Workflow” file; this way, Github will run your tests every time you commit ^_^
Below I’m using main
as the name of the main branch; if you use master
as the
name, then change that or otherwise the tests will not trigger automatically
after push!
# This workflow will do a clean install of dependencies and run tests
# For more information see: https://help.github.com/actions/language-and-framework-guides/
name: Lifemacs Loads Successfully
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ master ]
pull_request:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Set up Emacs
uses: purcell/[email protected]
with:
# The version of Emacs to install, e.g. "24.3", or "snapshot" for a recent development version.
version: 27.1 # optional
# Runs a single command using the runners shell
# - name: Run a one-line script
# run: echo Hello, world!
# Runs a set of commands using the runners shell
# - name: Run a multi-line script
# run: |
# echo Add other actions to build,
# echo test, and deploy your project.
- name: where am I and what is here
run: |
pwd
ls
- name: Attempting startup & basic test ...
run: time emacs -nw --batch --eval='(let
(
(user-emacs-directory default-directory))
(message "Default directory" )
(message default-directory)
(setq url-show-status nil)
(package-initialize)
(load-file "init.el")
(message "\n 🤤 Startup Successful! 🤩")
(when nil "It seems this results in a non-existent Agenda issue when run by GHA; ignoring for now."
(message "\nCheck we can do something basic, like getting pretty HTML from an Org file\n")
(find-file "init.org")
(org-html-export-to-html)
(message "\n 🤤 HTML file generated successfully! 🤩\n")))'
TODO: Build HTML export as a minimal test that things work as expected.
(find-file “init.org”) (org-html-export-to-html) (message “\n 🤤 HTML Export Successful! 🤩”)
(unless noninteractive …) ;; Use this to omit stuff from the Github Actions test
Also: Change to the conventional scrolling direction: If I pull my scroll down, I want to go down.
- Apple menu → System Preferences → Mouse → Tick the scroll direction option.
- Use brew to get Emacs:
#
# https://github.com/d12frosted/homebrew-emacs-plus $ brew tap d12frosted/emacs-plus $ brew reinstall emacs-plus@30 --with-xwidgets --with-imagemagick --with-dbus --with-debug --with-no-frame-refocus --with-native-comp --with-savchenkovaleriy-big-sur-3d-icon --with-poll
#
#
#
#
- Then make the command
emacs
available via the terminal —required if doing any melpa development.ln -s /opt/homebrew/opt/emacs-plus@30/Emacs.app /Applications
- Why
--with-imagemagick
?;; This lets us change the size of images when shown in Org-mode.
(setq org-image-actual-width nil)
Note that only the
:width
option is used for in-Org image preview. - Why
--with-xwidgets
?We get a full-fledged Internet browser within Emacs.
;; Clicking on a URL, or running M-x browse-url, should open the URL *within* Emacs. (setq browse-url-browser-function #'xwidget-webkit-browse-url) ;; (use-package xwwp) ;; Enhance the Emacs xwidget-webkit browser
Related: doc:goto-address-mode is useful whenever you have a buffer full of “http…” URLs (e.g.., a JSON file): It makes them into clickable buttons or via
C-c RET
.- How about EAF or nyxt?
An alternative to xwidget-webkit is EAF, sadly this does not work well with MacOS.
EAF essentially makes Emacs a window manager that runs other GUI apps —as such, EAF buffers are not classic Emacs buffers (and so your favourite text commands are useless).
There is also:
nyxt ≅ the web running common lisp instead of JS ≅ an Emacs backed-by common lisp - Tell me more about xwidget-webkit
I’ve found that the only two applications I regularly have open are Emacs and a browser —and Emacs has a modern browser, so might as well use that in Emacs.
- Downsides of Emacs as a browser: Some webpages, such as Slack, try to be an editor and so I’m using a Slack editor
insider a web browser inside an text editor (Emacs). As such, sometimes the lines between editor and browser need to
be explicitly demarcated; e.g., via
xwidget-webkit-edit-mode
. - “xwidget ≈ eXternal widget” lets us embed GTK widgets inside an Emacs window; e.g., we can insert fancy buttons via
xwidget-insert
, and a browser using doc:xwidget-webkit-browse-url. - For history and info on xwidget, see the original patch; See also: https://webkitgtk.org/
- Downsides of Emacs as a browser: Some webpages, such as Slack, try to be an editor and so I’m using a Slack editor
insider a web browser inside an text editor (Emacs). As such, sometimes the lines between editor and browser need to
be explicitly demarcated; e.g., via
- How about EAF or nyxt?
- Install git:
brew install git
- Get my Emacs setup:
rm -rf ~/.emacs.d; git clone https://github.com/alhassy/emacs.d.git ~/.emacs.d
Open Emacs and watch download and set up many other things … ^_^
This may take ~15 minutes —we install a massive LaTeX setup.
We get: Spell checker, dictionary, LaTeX + pygements, Dropbox, AG (for fast system-wide searching of a string with doc:helm-do-grep-ag, useful for finding definitions).
#
since they override the beloved Emacs M-<,>
keys.
For convenience, on a Mac, add the home (~/
) directory to the default file
navigator: Finder → Preferences → Sidebar, then select home 🏠.
If you notice any “file system access” concerns, give Emacs permissions to read
your files: General Settings → Security & Privacy → Full Disk Access → ⌘-M-g
(to
search) then enter /usr/bin/ruby
—Emacs is launched via a Ruby script in
MacOS.
Finally, see the Prose/Unicode section, we need to manually install the Symbol font for subscripts.
A raw code file is difficult to maintain, especially for a large system such as Emacs. Instead, we’re going with a ‘literate programming’ approach: The intialisation configuration is presented in an essay format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using org-mode —which is Emacs’ killer app.
Export In Emacs, press kbd:C-c_C-e_h_o to obtain an HTML webpage —/like this one!/— of the Org-mode markup; use kbd:C-c_C-e_l_o to obtain a PDF rendition.
You can try Org-mode notation and see how it renders live at: http://mooz.github.io/org-js/
You make a heading by writing * heading
at the start of a line, then you can
kbd:TAB to fold/unfold its contents. A table of contents, figures, tables can be
requested as follows:
# figures not implemented in the HTML backend
# The 𝓃 is optional and denotes headline depth
#+toc: headlines 𝓃
#+toc: figures
#+toc: tables
- Markup elements can be nested.
Syntax Result /Emphasise/
, italicsEmphasise *Strong*
, boldStrong */very strongly/*
, bold italics/very strongly/ =verbatim=
, monospaced typewriterverbatim
+deleted+
deleted_inserted_
inserted super^{script}ed
superscripted sub_{scripted}ed
subscripteded - Markup can span across multiple lines, by default no more than 2.
- In general, markup cannot be ‘in the middle’ of a word.
- New lines demarcate paragraphs
- Use
\\
to force line breaks without starting a new paragraph - Use at least 5 dashes,
-----
, to form a horizontal rule
badge:org-special-block-extras|2.0|informational|https://github.com/alhassy/org-special-block-extras|Gnu-Emacs
provides support for numerous other kinds of markup elements, such as red:hello
which becomes “ red:hello ”.
Working with tables
#+ATTR_HTML: :width 100%
#+name: my-tbl
#+caption: Example table
| Who? | What? |
|------+-------|
| me | Emacs |
| you | Org |
Note the horizontal rule makes a header row and is formed by typing | - then pressing kbd:TAB. You can kbd:TAB between cells.
- You can make an empty table with
C-c |
, which is just doc:org-table-create-or-convert-from-region, then give it row×column dimensions. - Any lines with comma-separated-values (CSV) can be turned into an Org table by
selecting the region and pressing
C-u C-c |
. (Any CSV file can thus be visualised nicely as an Org table). - Use
C-u C-u C-u C-c |
to make a table from values that are speared by a certain regular expression.
Working with links
Link syntax is [[source url][description]]
; e.g., we can refer to the above
table with [[my-tbl][woah]]
.
Likewise for images: file:path-to-image.
Mathematics
- Instead of
\[...\]
, which displays a formula on its own line, centred, use$...$
to show a formula inline. - Captioned equations are numbered and can be referenced via links, as shown below.
Source code
The labels (ref:name)
refer to the lines in the source code and can be
referenced with link syntax: [[(name)]]
. Hovering over the link, in the HTML
export, will dynamically highlight the corresponding line of code. To strip-out
the labels from the displayed block, use -r -n
in the header so it becomes
#+begin_src C -r -n
, now the references become line numbers.
Another reason to use Org:
If you use :results raw
, you obtain dynamic templates that may use Org-markup:
♯+RESULTS:
bold 12345 (strikethrough) slanted
The #+RESULTS:
is obtained by pressing kbd:C-c_C-c on the src
block, to execute
it and obtain its result.
Also: Notice that a C program can be run without a main
;-)
That is, we can write code in between prose that is intended to be read like an essay:
- badge:Lifemacs|CheatSheet|informational|https://alhassy.github.io/emacs.d/CheatSheet.pdf|Gnu-Emacs ⇒ A brief reference of Emacs keybindings; 2 pages
- Elisp cheat sheet ⇒ A compact Emacs Lisp Reference; 7 pages
Single source of truth: This mini-tutorial can be included into other Org files by declaring
#+include: ~/.emacs.d/init.org::#Mini-tutorial-on-Org-mode |
For more, see https://orgmode.org/features.html.
Emacs is a flexible platform for developing end-user applications —unfortunately it is generally perceived as merely a text editor. Some people use it specifically for one or two applications.
For example, writers use it as an interface for Org-mode and others use it as an interface for version control with Magit. Org is an organisation tool that can be used for typesetting which subsumes LaTeX, generating many different formats –html, latex, pdf, etc– from a single source, keeping track of schedules & task management, blogging, habit tracking, personal information management tool, and much more. Moreover, its syntax is so natural that most people use it without even knowing! For me, Org allows me to do literate programming: I can program and document at the same time, with no need to seperate the two tasks and with the ability to generate multiple formats and files from a single file.
If you are a professional writer…Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish. —Neal Stephenson, In the beginning was the command line
- Extensible ⇒ IDEs are generally optimised for one framework, unlike Emacs!
- You can program Emacs to automate anything you want.
- Hence, it’s an environment, not just an editor.
- ⇒ Unified keybinding across all tools in your environment.
Users are given a high-level full-featured programming language, not just a small configuration language. For the non-programmers, there is Custom, a friendly point-and-click customisation interface.
- Self Documented ⇒ Simply kbd:M-x info-apropos or kbd:C-h_d to search all manuals or look up any function provided by Emacs!
- Mature ⇒ tool with over 40 years of user created features
- Plugins for nearly everything!
- No distinction between built-ins and user-defined features! (Lisp!)
- You can alter others’ code without even touching the source.
- Advising functions and ‘hooking’ functionality onto events.
- Free software ⇒ It will never die!
- Emacs is one of the oldest open source projects still under developement.
Of course Emacs comes with the basic features of a text editor, but it is much more; for example, it comes with a powerful notion of ‘undo’: Basic text editors have a single stream of undo, yet in Emacs, we have a tree —when we undo and make new edits, we branch off in our editing stream as if our text was being version controlled as we type! We can even switch between such branches! That is, while other editors have a single-item clipboard, Emacs has an infinite clipboard that allows undoing to any historical state.
;; Allow tree-semantics for undo operations.
(use-package undo-tree
:bind ("C-x u" . undo-tree-visualize)
:config
;; Each node in the undo tree should have a timestamp.
(setq undo-tree-visualizer-timestamps t)
;; Show a diff window displaying changes between undo nodes.
(setq undo-tree-visualizer-diff t)
;; Prevent undo tree files from polluting your git repo
(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))))
;; Always have it on
(global-undo-tree-mode)
;; Execute (undo-tree-visualize) then navigate along the tree to witness
;; changes being made to your file live!
( The above snippet has a noweb-ref
: It is presented here in a natural
position, but is only executable once use-package
is setup and so it
is weaved there! We can present code in any order and tangle it to
the order the compilers need it to be! )
Emacs is an extensible editor: You can make it into the editor of your dreams!
You can make it suited to your personal needs.
If there’s a feature you would like, a behaviour your desire, you can simply code that into Emacs with
a bit of Lisp. As a programming language enthusiast, for me Emacs is my default Lisp interpreter
and a customisable IDE that I use for other programming languages
–such as C, Haskell, Agda, Lisp, and Prolog.
Moreover, being a Lisp interpreter, we can alter the look and feel of Emacs live, without having
to restart it –e.g., press kbd:C-x_C-e after the final parenthesis of (scroll-bar-mode 0)
to run the code that removes the scroll-bar.
I use Emacs every day. I rarely notice it. But when I do, it usually brings me joy. ─Norman Walsh
I have used Emacs as an interface for developing cheat sheets, for making my blog, and as an application for ‘interactively learning C’. If anything Emacs is more like an OS than just a text editor –“living within Emacs” provides an abstraction over whatever operating system my machine has: It’s so easy to take everything with me. Moreover, the desire to mould Emacs to my needs has made me a better programmer: I am now a more literate programmer and, due to Elisp’s documentation-oriented nature, I actually take the time and effort to make meaningful documentation –even when the project is private and will likely only be seen by me.
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. )Seeing Emacs as an editor is like seeing a car as a seating-accommodation. – Karl Voit
In the case of Emacs the boundary between user and programmer is blurred as adapting the environment to one’s needs is already an act of programming with a very low barrier to entry. —rekado
Emacs is not just an editor, but a host for running Lisp applications!
For example, Emacs is shipped as a language-specific IDE to a number of communities —e.g., Oz, Common Lisp, and, most notably, Agda. Emacs is a great IDE for a language —one just needs to provide a ‘major mode’ and will then have syntax highlighting, code compleition, jumping to definitions, etc. There is no need to make an IDE from scratch.
Emacs has ways to represent all kinds of information as text.E.g., if want to make a regular expression rename of files in a directory, there’s no need to learn about a batch renaming tool: kbd:M-x dired ⟨RET⟩ M-x wdired-change-to-wdired-mode now simply perform a usual find-and-replace, then save with the usual kbd:C-x_C-s to effect the changes!
Likewise for other system utilities and services (•̀ᴗ•́)و
Moreover, as will be shown below, you can literally use Emacs anywhere for textually input in your operating system –no copy-paste required.
Suppose you wrote a paragraph of text, and wanted to ‘border’ it up for emphasies in hypens. Using the mouse to navigate along with a copy-paste of the hypens is vastely inferior to the incantation <a href=”kbd:M-{ C-u 80 - RET M-} C-u 80 - RET”>kbd:M-{ C-u 80 - RET M-} C-u 80 - RET. If we want to border up the previous 𝓃-many paragraphs, we simply prefix kbd:M-{,} above with kbd:C-u_𝓃 —a manual approach would have us count 𝓃 and slowly scroll. ( Exercise: What incantation of keys ‘underlines’ the current line with only the necessary amount of dashes? —Solution in the source file. )
⇒ MacOS supports many Emacs shortcuts, system-wide, such as kbd:C-a/e, kbd:C-d, kbd:C-k/y, kbd:C-o, kbd:C-p/n and even kbd:C-t for transposing two characters. ⇐
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.
- The Emacs Tour
- How to Learn Emacs: A Hand-drawn One-pager for Beginners / A visual tutorial
- Video Series on Why Emacs Rocks —catch the enthusiasm!
- EMACS: The Extensible, Customizable Display Editor
“The programmable editor is an outstanding opportunity to learn to program!”
- What is free software?
- Emacs org-mode examples and cookbook
- An Opinionated Emacs guide for newbies and beyond
- Emacs Mini-Manual, Part I of III
- Org and R Programming —a tutorial on literate programming, e.g., evaluating code within
src
bloc. - Reference cards for GNU Emacs, Org-mode, and Elisp.
- “When did you start using Emacs” discussion on Reddit
- “How to Learn Emacs”
- The Org-mode Reference Manual or Worg: Community-Written Docs which includes a meta-tutorial.
- Awesome Emacs: A community driven list of useful Emacs packages, libraries and others.
- A list of people’s nice emacs config files
- Read Lisp, Tweak Emacs: How to read Emacs Lisp so that you can customize Emacs
- Why Racket? Why Lisp?
—If eye-candy, a sleek and beautiful GUI, would entice you then consider starting with spacemacs. Here’s a helpful installation video, after which you may want to watch Org-mode in Spacemacs tutorial—
Remember: Emacs is a flexible platform for developing end-user applications; e.g., this configuration file is at its core an Emacs Lisp program that yields the editor of my dreams –it encourages me to grow and to be creative, and I hope the same for all who use it; moreover, it reflects my personality such as what I value and what I neglect in my workflow.
Finally, here’s some fun commands to try out: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
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…
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 lets us do that!
Here's one definition,
#+begin_src haskell :noweb-ref code-from-other-places :tangle no
g = ⋯
#+end_src
then the other.
#+begin_src haskell :noweb-ref code-from-other-places :tangle no
h = ⋯
#+end_src
Of-course, we might also want a preamble:
#+BEGIN_SRC haskell :tangle myprogram.hs
import ⋯
#+END_SRC
We can now tangle together the tagged code blocks in the order we want.
#+BEGIN_SRC haskell :tangle myprogram.hs :comments none :noweb yes
<<code-from-other-places>>
<<defn-of-f>>
#+END_SRC
( You can press “C-c C-v C-v” to see what this block expands into! )
Now kbd:C-c_C-v_C-t (doc:org-babel-tangle) yields a file named myprogram.hs
with contents in an order
amicable to a machine.
import ⋯
g = ⋯
h = ⋯
f = h ∘ g
Interestingly, unlike certain languages, Haskell doesn’t care too much about declaration order.
Warning! If we have different language blocks tangled to the same file, then
they are tangled alphabetically —e.g., if one of the blocks is marked
emacs-lisp
then its contents will be the very first one in the resulting source
file, since emacs-lisp
begins with e
which is alphabetically before h
of
haskell
.
- Introduction to Literate Programming with Org-mode
- Emacs org-mode examples and cookbook
- Literate Config —Online booklet
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!
Let’s decide on where we want to setup our declarations for personalising Emacs
to our needs. Then, let’s bootstrap Emacs’ primitive packaging mechanism with a
slick interface —which not only installs Emacs packages but also programs at
the operating system level, all from inside Emacs! Finally, let’s declare who
we are and use that to setup Emacs email service.
Emacs is extensible: When Emacs is started, it tries to load a user’s Lisp
program known as an initialisation (‘init’) file which specifies how Emacs
should look and behave for you. Emacs looks for the init file using the
filenames ~/.emacs.el
, ~/.emacs
, or ~/.emacs.d/init.el
—it looks for the first
one that exists, in that order; at least it does so on my machine. Below we’ll
avoid any confusion by ensuring that only one of them is in our system.
Regardless, execute kbd:C-h o user-init-file to see the name of the init file
loaded. Having no init file is tantamount to have an empty init file.
- One can read about the various Emacs initialisation files online or within Emacs by the sequence kbd:C-h i m emacs RET i init file RET.
- A friendly tutorial on ‘beginning a
.emacs
file’ can be read online or within Emacs by kbd:C-h i m emacs lisp intro RET i .emacs RET. - After inserting some lisp code, such as
(set-background-color "salmon")
, and saving, one can load the changes with kbd:M-x eval-buffer, doc:eval-buffer. - In a terminal, use
emacs -Q
to open emacs without any initialisation files.
Besides writing Lisp in an init file, one may use Emacs’ customisation interface, kbd:M-x customize: Point and click to change Emacs to your needs. The resulting customisations are, by default, automatically thrown into your init file —=~/.emacs= is created for you if you have no init file. This interface is great for beginners.
#
We shall use ~/.emacs.d/init.el
as the initialisation file so that all of our
Emacs related files live in the same directory: ~/.emacs.d/
.
A raw code file is difficult to maintain, especially for a large system such as Emacs. Instead, we’re going with a ‘literate programming’ approach: The intialisation configuration is presented in an essay format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using org-mode —/Emacs’ killer app/— which is discussed in great detail later on.
~/.emacs
;; else; looks for this one;
(set-background-color "plum4")
(message-box ".emacs says hello")
~/.emacs.d/init.el
;; Finally, if neither are found; it looks for this one.
(set-background-color "salmon")
(message-box ".emacs.d/init.el says hello")
Now restart your Emacs to see how there super tiny initilaisation files affect your editor. Delete some of these files in-order for others to take effect!
Speaking of local variables, let’s always load ones we’ve already marked as safe —see the bottom of the source of this file for an example of local variables. ( At one point, all my files had locals! )(setq enable-local-variables :safe)
(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)
There are a few ways to install packages —run kbd:C-h_C-e for a short overview. The easiest, for a beginner, is to use the command doc:package-list-packages then find the desired package, press kbd:i to mark it for installation, then install all marked packages by pressing kbd:x.
- Interactively: kbd:M-x list-packages to see all melpa packages that can install
- Press kbd:Enter on a package to see its description.
- Or more quickly, to install, say, unicode fonts: <a href=”kbd:M-x package-install RET unicode-fonts RET”>kbd:M-x package-install RET unicode-fonts RET.
“From rags to riches”: Recently I switched to Mac —first time trying the OS.
I had to do a few package-install
’s and it was annoying. I’m looking for the
best way to package my Emacs installation —including my installed packages and
configuration— so that I can quickly install it anywhere, say if I go to
another machine. It seems doc:use-package allows me to configure and auto
install packages. On a new machine, when I clone my .emacs.d
and start Emacs,
on the first start it should automatically install and compile all of my
packages through use-package
when it detects they’re missing. ♥‿♥
First we load package
, the built-in package manager. It is by default only
connected to the GNU ELPA (Emacs Lisp Package Archive) repository, so we
extended it with other popular repositories; such as the much larger MELPA
(Milkypostman’s ELPA) —it builds packages directly from the source-code
repositories of developers rather than having all packages in one repository.
;; Make all commands of the “package” module present.
(require 'package)
;; Internet repositories for new packages.
(setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "http://melpa.org/packages/")))
;; Update local list of available packages:
;; Get descriptions of all configured ELPA packages,
;; and make them available for download.
(package-refresh-contents)
- All installed packages are placed, by default, in
~/.emacs.d/elpa
. - Neato: If one module requires others to run, they will be installed automatically.
The declarative configuration tool use-package is a macro/interface that manages our packages and the way they interact.
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
We can now invoke (use-package XYZ :ensure t)
which should check for the XYZ
package and makes sure it is accessible. If the file is not on our system, the
:ensure t
part tells use-package
to download it —using the built-in package
manager— and place it somewhere accessible, in ~/.emacs.d/elpa/
by default.
By default we would like to download packages, since I do not plan on installing
them manually by downloading Lisp files and placing them in the correct places
on my system.
(setq use-package-always-ensure t)
Notice that doc:use-package allows us to tersely organise a package’s
configuration —and that it is not a package manger, but we can make it one by
having it automatically install modules, when needed, using :ensure t
.
We now bootstrap use-package
.
The use of :ensure t
only installs absent modules, but it does no updating.
Let’s set up an auto-update mechanism.
(use-package auto-package-update
:config
;; Delete residual old versions
(setq auto-package-update-delete-old-versions t)
;; Do not bother me when updates have taken place.
(setq auto-package-update-hide-results t)
;; Update installed packages at startup if there is an update pending.
(auto-package-update-maybe))
Here’s another example use of use-package
. Later on, I have a “show recent files
pop-up” command set to C-x C-r
; but what if I forget? This mode shows me all key
completions when I type C-x
, for example. Moreover, I will be shown other
commands I did not know about! Neato :-)
;; Making it easier to discover Emacs key presses.
(use-package which-key
:config (which-key-mode)
(which-key-setup-side-window-bottom)
(setq which-key-idle-delay 0.05))
⟨ Honestly, I seldom even acknowledge this pop-up; but it’s always nice to show to people when I’m promoting Emacs. ⟩
Here are other packages that I want to be installed onto my machine.
(use-package dash) ;; “A modern list library for Emacs”
(use-package s) ;; “The long lost Emacs string manipulation library”.
(use-package f) ;; Library for working with system files; ;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden?
(defvar my/personal-machine?
(equal "Musa’s MacBook Air " (s-collapse-whitespace (shell-command-to-string "scutil --get ComputerName")))
"Is this my personal machine, or my work machine?
At one point, on my work machine I run the following command to give the machine a sensible name.
sudo scutil --set ComputerName work-machine
dscacheutil -flushcache")
(defvar my/work-machine? (not my/personal-machine?))
Note:
- dash: “A modern list library for Emacs”
- E.g.,
(--filter (> it 10) (list 8 9 10 11 12))
- 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>>
You can press kbd:C-c_C-v_C-v, doc:org-babel-expand-src-block, to see what this block expands into —which is what was shown above.
link-here:Quelpa
Quelpa allows us to build Emacs packages directly from source repositories. It
derives its name from the German word Quelle, for souce [code], adjoined to
ELPA. Its use-package
interface allows us to use use-package
like normal but
when we want to install a file from souce we use the keyword :quelpa
.
(use-package quelpa
:custom (quelpa-upgrade-p t "Always try to update packages")
:config
;; Get ‘quelpa-use-package’ via ‘quelpa’
(quelpa
'(quelpa-use-package
:fetcher git
:url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package))
Let’s use this to obtain an improved info-mode from the EmacsWiki. [Disabled for now]
(use-package info+
:quelpa (info+ :fetcher wiki :url "https://www.emacswiki.org/emacs/info%2b.el"))
Sometimes Emacs packages depend on existing system binaries, use-package
let’s
us ensure these exist using the :ensure-system-package
keyword extension.
- This is like
:ensure t
but operates at the OS level and uses your default OS package manager. - It has multiple features.
Let’s obtain the extension.
;; Auto installing OS system packages
(use-package system-packages)
;; Ensure our operating system is always up to date.
;; This is run whenever we open Emacs & so wont take long if we're up to date.
;; It happens in the background ^_^
;;
;; After 5 seconds of being idle, after starting up.
(run-with-idle-timer 5 nil #'system-packages-update) ;; ≈ (async-shell-command "brew update && brew upgrade")
(defvar my/installed-packages
(shell-command-to-string "brew list")
"What is on my machine already?
Sometimes when I install a GUI based application and do not have access to it everywhere in my path,
it may seem that I do not have that application installed. For instance,
(system-packages-package-installed-p \"google-chrome\")
returns nil, even though Google Chrome is on my machine.
As such, we advise the `system-packages-ensure' installtion method to only do
installs of packages that are not in our `my/installed-packages' listing.
")
(advice-add 'system-packages-ensure :before-until (lambda (pkg) (s-contains-p pkg my/installed-packages)))
;; Please don't bother me when shell buffer names are in use, just make a new buffer.
(setq async-shell-command-buffer 'new-buffer)
;; Display the output buffer for asynchronous shell commands only when the
;; command generates output.
(setq async-shell-command-display-buffer nil)
;; Don't ask me if I want to kill a buffer with a live process attached to it;
;; just kill it please.
(setq kill-buffer-query-functions
(remq 'process-kill-buffer-query-function
kill-buffer-query-functions))
After an update to Mac OS, one may need to restore file system access privileges to Emacs.
Here’s an example use for Emacs packages that require OS packages:
(shell-command-to-string "type rg") ;; ⇒ rg not found
(use-package rg
:ensure-system-package rg) ;; ⇒ There's a buffer *system-packages*
;; installing this tool at the OS level!
If you look at the *Messages*
buffer, via C-h e
, on my machine it says
brew install rg: finished
—it uses brew
which is my OS package manager!
- The use-package-ensure-system-package documentation for a flurry of use cases.
The extension makes use of system-packages; see its documentation to learn
more about managing installed OS packages from within Emacs. This is itself
a powerful tool, however it’s interface M-x system-packages-install
leaves much
to be desired —namely, tab-compleition listing all available packages,
seeing their descriptions, and visiting their webpages.
This is remedied by M-x helm-system-packages then RET
to see a system
package’s description, or TAB
for the other features!
This is so cool!
;; An Emacs-based interface to the package manager of your operating system.
(use-package helm-system-packages)
The Helm counterpart is great for discovarability, whereas
the plain system-packages
is great for programmability.
(setq system-packages-noconfirm :do-not-prompt-me-about-confirms)
;; After 1 minute after startup, kill all buffers created by ensuring system
;; packages are present.
(run-with-timer 60 nil
(lambda () (kill-matching-buffers ".*system-packages.*" t :kill-without-confirmation)))
It is tedious to arrange my program windows manually, and as such I love tiling window managers, which automatically arrange them. I had been using xmonad until recently when I obtained a Mac machine and now use Amethyst —“Tiling window manager for macOS along the lines of xmonad.”
;; Unlike the Helm variant, we need to specify our OS pacman.
(setq system-packages-package-manager 'brew)
;; If the given system package doesn't exist; install it.
;; (system-packages-ensure "amethyst") ;; This is a MacOS specific package.
;; (ignore-errors (system-packages-ensure "google-chrome")) ;; My choice of web browser
;; (system-packages-ensure "microsoft-teams") ;; For remote work meetings
;; Gif maker; needs privileges to capture screen.
;;
;; ⇒ Move the screen capture frame while recording.
;; ⇒ Pause and restart recording, with optional inserted text messages.
;; ⇒ Global hotkey (shift+space) to toggle pausing while recording
(system-packages-ensure "licecap") ;; Use: ⌘-SPACE licecap
;; Pack, ship and run any application as a lightweight container
;; (system-packages-ensure "docker")
;; Free universal database tool and SQL client
;; (system-packages-ensure "dbeaver-community")
;; Kubernetes IDE
;; (system-packages-ensure "lens")
;; Platform built on V8 to build network applications
;; Also known as: node.js, node@16, nodejs, npm
(system-packages-ensure "node") ;; https://nodejs.org/
;; Nice: https://nodesource.com/blog/an-absolute-beginners-guide-to-using-npm/
;; Manage multiple Node.js versions
;; (shell-command "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash")
;; According to https://github.com/nvm-sh/nvm, nvm shouldn't be installed via brew.
;; ;; Use “brew cask install” instead of “brew install” for installing programs.;
;; (setf (nth 2 (assoc 'brew system-packages-supported-package-managers))
;; '(install . "brew cask install"))
Amethyst requires some more setup: Open its preferences, then…
- Then select:
Mouse: Focus follows mouse
. - Also:
Shortcuts
, then disable ‘increase/decrease main pane count’ bindings since they override the beloved EmacsM-<,>
keys.
Neato! Now I can live in Emacs even more ^_^
(Open Scripting Architecture (OSA) Scripts) Amethyst is great, but it has a problem of randomly not working. Unfortunatley it has no command line interface, so let’s make one in Emacs: Now kbd:⌘-a_r relaunches Amethyst.
We use the osascript
command to tell
the System Events application
to issue
keystrokes to other applications. I found out about by Googling “How to send
keystrokes from terminal”.
(defun amethyst/cycle-layout ()
(interactive)
(shell-command "osascript -e 'tell application \"System Events\" to keystroke space using {shift down, option down}'"))
If you get:
36:51: execution error: System Events got an error: osascript is not allowed to send keystrokes. (1002)
Then: Go to Security & Privacy -> Privacy tab -> Accessibility -> Add osascript (/usr/bin/osascript)
You may need to restart Emacs.
Reads:
I enter “⌘” using a TeX input method setup below (called “Agda”).
;; By default, say, (async-shell-command "date") produces a buffer
;; with the result. In general, such commands in my init.el are for
;; updating/installing things to make sure I have the same up-to-date
;; setup where-ever I use my Emacs. As such, I don't need to see such buffers.
(add-to-list 'display-buffer-alist
'("\\*Async Shell Command\\*.*" display-buffer-no-window))
;; For an approach that does not inhibit async-shell-command this way,
;; see https://emacs.stackexchange.com/questions/299/how-can-i-run-an-async-process-in-the-background-without-popping-up-a-buffer
For one reason or another, on OS X it seems that an Emacs instance
begun from the terminal may not inherit the terminal’s environment
variables, thus making it difficult to use utilities like pdflatex
when Org-mode attempts to produce a PDF.
(use-package exec-path-from-shell
:init
(when (memq window-system '(mac ns x))
(exec-path-from-shell-initialize)))
See the exec-path-from-shell documentation for setting other environment variables.
;; https://emacs.stackexchange.com/questions/4090/org-mode-cannot-find-pdflatex-using-mac-os
(defun set-exec-path-from-shell-PATH ()
"Sets the exec-path to the same value used by the user shell"
(let ((path-from-shell
(replace-regexp-in-string
"[[:space:]\n]*$" ""
(shell-command-to-string "$SHELL -l -c 'echo $PATH'"))))
(setenv "PATH" path-from-shell)
(setq exec-path (split-string path-from-shell path-separator))))
;; call function now
(set-exec-path-from-shell-PATH)
Sometimes I wish to close then reopen Emacs; unsurprisingly someone’s thought of implementing that.
;; Provides only the command “restart-emacs”.
(use-package restart-emacs
;; If I ever close Emacs, it's likely because I want to restart it.
:bind ("C-x C-c" . restart-emacs)
;; Let's define an alias so there's no need to remember the order.
:config (defalias 'emacs-restart #'restart-emacs))
The following is disabled. I found it a nuisance to have my files open across sessions —If I’m closing Emacs, it’s for a good reason.
;; Keep open files open across sessions. (desktop-save-mode 1) (setq desktop-restore-eager 10)
Instead, let’s try the following: When you visit a file, point goes to the last place where it was when you previously visited the same file.
(setq-default save-place t)
(setq save-place-file "~/.emacs.d/etc/saveplace")
Whenever we have a choice to make from a list, Helm provides possible
completions and narrows the list of choices as we type. This is extremely
helpful for when switching between buffers, C-x b
, and discovering & learning
about other commands! E.g., press M-x
to see recently executed commands and
other possible commands! “Fuzzy finding”: Press M-x
and just start typing,
methods mentioning what you’ve typed are suddenly listed! Moreover, C-c i
(doc:helm-imenu) will show you the headers in an Org file or the top-level
variables/functions/types when programming. Finally, whenever a Helm session has
started, toggle follow-mode with C-c C-f
to obtain contextual-awareness; e.g.,
C-c i RET C-c C-f
will change your screen as you scroll through the menu.
(A killer feature is doc:helm-do-grep-ag which will do a search in your whole project, file tree).
Remembrance comes with time, until then ask Emacs! |
Try and be grateful!
(use-package helm
:init (helm-mode t)
:bind (("M-x" . helm-M-x)
("C-x C-f" . helm-find-files)
("C-x b" . helm-mini) ;; See buffers & recent files; more useful.
("C-x r b" . helm-filtered-bookmarks)
("C-x C-r" . helm-recentf) ;; Search for recently edited files
("C-c i" . helm-imenu) ;; C.f. “C-x t m” (imenu-list)
;; ("C-u C-c i" . imenu-list) ;; TODO FIXME Key sequence C-u C-c i starts with non-prefix key C-u
("C-h a" . helm-apropos)
;; Look at what was cut recently & paste it in.
("M-y" . helm-show-kill-ring)
("C-x C-x" . helm-all-mark-rings)
:map helm-map
;; We can list ‘actions’ on the currently selected item by C-z.
("C-z" . helm-select-action)
;; Let's keep tab-completetion anyhow.
("TAB" . helm-execute-persistent-action)
("<tab>" . helm-execute-persistent-action)))
;; Show me nice file icons when using, say, “C-x C-f” or “C-x b”
;; (use-package helm-icons
;; :custom (helm-icons-provider 'all-the-icons)
;; :config (helm-icons-enable))
;; When I want to see the TOC of an Org file, show me down to 3 subheadings.
(setq org-imenu-depth 7)
Helm provides generic functions for completions to replace tab-completion in Emacs with no loss of functionality.
- The
execute-extended-command
, the default “M-x”, is replaced withhelm-M-x
which shows possible command completions.- If we want the
M-x
minibuffer to appear at the top of the screen, or middle, we can use emacs-mini-frame as shown beautifully here. I likehelm-M-x
, for now.
Likewise with
apropos
, which is helpful for looking up commands. It shows all meaningful Lisp symbols whose names match a given pattern. - If we want the
- The ‘Helm-mini’,
C-x b
, shows all buffers, recently opened files, bookmarks, and allows us to create new bookmarks and buffers! - The ‘Helm-imenu’,
C-c i
, yields a a menu of all “top-level items” in a file; e.g., functions and constants in source code or headers in an org-mode file.⟳ Nifty way to familarise yourself with a new code base, or one from a while ago.
- When Helm is active,
C-x
lists possible course of actions on the currently selected item.
When helm-mode
is enabled, even help commands make use of it.
E.g., C-h o
runs describe-symbol
for the symbol at point,
and C-h w
runs where-is
to find the key binding of the symbol at point.
Both show a pop-up of other possible commands.
Here’s a nifty tutorial: A package in a league of its own: Helm
Let’s ensure C-x b
shows us: Current buffers, recent files, and bookmarks
—as well as the ability to create bookmarks, which is via C-x r b
manually.
For example, I press C-x b
then type any string and will have the option of
making that a bookmark referring to the current location I’m working in, or
jump to it if it’s an existing bookmark, or make a buffer with that name,
or find a file with that name.
(setq helm-mini-default-sources '(helm-source-buffers-list
helm-source-recentf
helm-source-bookmarks
helm-source-bookmark-set
helm-source-buffer-not-found))
Incidentally, Helm even provides an interface for the top
program via
helm-top
. It also serves as an interface to popular search engines
and over 100 websites such as google, stackoverflow, ctan
, and arxiv
.
(system-packages-ensure "surfraw")
; ⇒ “M-x helm-surfraw” or “C-x c s”
If we want to perform a google search, with interactive suggestions,
then invoke helm-google-suggest
—which can be acted for other serves,
such as Wikipedia or Youtube by C-z
. For more google specific options,
there is the google-this
package.
Let’s switch to a powerful searching mechanism – helm-swoop. It allows us to
not only search the current buffer but also the other buffers and to make live
edits by pressing C-c C-e
when a search buffer exists. Incidentally, executing
C-s
on a word, region, will search for that particular word, region; then make
changes with C-c C-e
and apply them by C-x C-s
.
(use-package helm-swoop
:bind (("C-s" . 'helm-swoop) ;; search current buffer
("C-M-s" . 'helm-multi-swoop-all) ;; Search all buffer
;; Go back to last position where ‘helm-swoop’ was called
("C-S-s" . 'helm-swoop-back-to-last-point)
;; swoop doesn't work with PDFs, use Emacs' default isearch instead.
; :map pdf-view-mode-map ("C-s" . isearch-forward)
)
:custom (helm-swoop-speed-or-color nil "Give up colour for speed.")
(helm-swoop-split-with-multiple-windows nil "Do not split window inside the current window."))
C-u 𝓃 C-s
does a search but showing 𝓃 contextual lines!helm-multi-swoop-all
,C-M-s
, lets us grep files anywhere!
;; If there is no symbol at the cursor, use the last used words instead.
(setq helm-swoop-pre-input-function
(lambda ()
(let (($pre-input (thing-at-point 'symbol)))
(if (eq (length $pre-input) 0)
helm-swoop-pattern ;; this variable keeps the last used words
$pre-input))))
Press M-i
after a search has executed to enable it for all buffers.
We can also limit our search to org files, or buffers of the same mode, or buffers belonging to the same project!
Note that on the Mac, I can still perform default Emacs search using Cmd+f.
- There is a super duper neato search capability we can utilise –
swiper
. - We also use the
counsel
, a collection of completion,ivy
, enhanced versions of common Emacs commands.
(use-package ivy ;; More powerful but ugly looking completetion framework.
;; Helm is prettier.
;; Needed for Counsel, below.
:config
;; add ‘recentf-mode’ and bookmarks to ‘ivy-switch-buffer’.
(setq ivy-use-virtual-buffers t)
;; number of result lines to display
(setq ivy-height 10)
;; does not count candidates
(setq ivy-count-format "")
;; no regexp by default
(setq ivy-initial-inputs-alist nil))
(use-package counsel
:bind* ; load when pressed
(("C-s" . swiper)
;; ("C-x C-r" . counsel-recentf) ; search for recently edited
("<f1> l" . counsel-find-library) ; find an Emacs Lisp library
("<f2> u" . counsel-unicode-char))) ; insert a unicode symbol using a pop-up
swiper
is an alternative to Emacs’ default incremental search.
It shows an overview of all matches –navigate using arrow keys.
There is also a helm based version of swiper.
Lets also use doc:helm-do-grep-ag (kbd:C-x c M-g a) search all files in the current directory for a particular (regexp) string
- Shows matches live as you type
- Very helpful when looking for a definition of something
(system-packages-ensure "ag")
Marking my place when I jump around
Let’s use M-m
to get a nice menu of places we’ve been recently.
;; Save/mark a location with “C-u M-m”, jump back to it with “M-m”.
(bind-key* "M-m"
(lambda ()
(interactive)
(if (not current-prefix-arg)
(helm-mark-ring)
(push-mark)
(message "[To return to this location, press M-m] ∷ %s"
(s-trim (substring-no-properties (thing-at-point 'line)))))))
Finally, note that there is now a M-x helm-info
command to show documentation,
possibly with examples, of the packages installed. For example,
M-x helm-info RET dash RET -parition RET
to see how the parition function from the
dash library works via examples ;-)
;; Make `links' from elisp symbols (quoted functions, variables and fonts) in Gnu-Emacs Info viewer to their help documentation.
(use-package inform :config (require 'inform))
(shell-command "rm ~/.emacs.d/elpa/org-9.3/*.elc")
(byte-recompile-directory "~/.emacs.d/elpa/org-9.3/")
Easiest way get latest version of org-mode:
M-x package-list-packages
, then select org
, then install
.
#
Let’s conclude this ‘boot-up’ by getting Emacs’ killer app, Org-mode, setup; along with the extras that allow us to ignore heading names, but still utilise their contents —e.g., such as a heading named ‘preamble’ that contains org-mode setup for a file.
(use-package emacs
:ensure org-contrib
:config (require 'ox-extra)
(ox-extras-activate '(ignore-headlines)))
org-plus-contrib
contain the files that are included with Emacs plus all
contributions from the org-mode repository.
- Use the
:ignore:
tag on headlines you’d like to have ignored, while not ignoring their content. - Use the
:noexport:
tag to omit a headline and its contents.
;; Replace the content marker, “⋯”, with a nice unicode arrow.
(setq org-ellipsis " ⮛")
;; Other candidates:
;; (setq org-ellipsis " 📖")
;; (setq org-ellipsis " ◦◦◦")
;; (setq org-ellipsis " ⟨🫣⟩")
;; (setq org-ellipsis " ⟨👀⟩")
;; (setq org-ellipsis " ⤵")
;; Fold all source blocks on startup.
(setq org-hide-block-startup t)
;; Lists may be labelled with letters.
(setq org-list-allow-alphabetical t)
;; Avoid accidentally editing folded regions, say by adding text after an Org “⋯”.
(setq org-catch-invisible-edits 'show)
;; I use indentation-sensitive programming languages.
;; Tangling should preserve my indentation.
(setq org-src-preserve-indentation t)
;; Tab should do indent in code blocks
(setq org-src-tab-acts-natively t)
;; Give quote and verse blocks a nice look.
(setq org-fontify-quote-and-verse-blocks t)
;; Pressing ENTER on a link should follow it.
(setq org-return-follows-link t)
I rarely use tables, but here is a useful Org-Mode Table Editing Cheatsheet and a friendly tutorial.
Moreover, since I end up using org-mode most of the time, let’s make that the default mode.
(setq initial-major-mode 'org-mode)
Finally, let’s get some extra Org-mode mark-up goodies, such as kbd:C-c_C-e
which renders as kbd:C-c_C-e. Documentations and screenshots at:
https://alhassy.github.io/org-special-block-extras/
(defun org-special-block-extras-short-names ())
;;
;; org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks
(setq o--supported-blocks nil)
;; TODO org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks
;;
(use-package org-special-block-extras
:hook (org-mode . org-special-block-extras-mode)
:custom
;; The places where I keep my ‘#+documentation’
(org-special-block-extras--docs-libraries
'("~/org-special-block-extras/documentation.org"))
;; Disable the in-Emacs fancy-links feature?
(org-special-block-extras-fancy-links
'(elisp badge kbd link-here doc tweet))
;; Details heading “flash pink” whenever the user hovers over them?
(org-html-head-extra (concat org-html-head-extra "<style> summary:hover {background:pink;} </style>"))
;; The message prefixing a ‘tweet:url’ badge
(org-special-block-extras-link-twitter-excitement
"This looks super neat (•̀ᴗ•́)و:")
:config
;; Use short names like ‘defblock’ instead of the fully qualified name
;; ‘org-special-block-extras--defblock’
(org-special-block-extras-short-names))
;; Let's execute Lisp code with links, as in “elisp:view-hello-file”.
(setq org-confirm-elisp-link-function nil)
With the following incantation, we name our files 𝒳.𝒴.gpg
where 𝒳 is the file
name and 𝒴 is the usual extension, then upon save we will be prompted for an
encryption method, we can press kbd:Enter on OK
to just provide a password for
that file. You can open that file without the passphrase for a limited amount of
time —i.e., it’s cached, saved, for your current computing session until
logout— or force authentication by invoking gpgconf --kill gpg-agent
.
(system-packages-ensure "gnupg") ;; i.e., brew install gnupg
;; “epa” ≈ EasyPG Assistant
;; Need the following in init to have gpg working fine:
;; force Emacs to use its own internal password prompt instead of an external pin entry program.
(setq epa-pinentry-mode 'loopback)
;; https://emacs.stackexchange.com/questions/12212/how-to-type-the-password-of-a-gpg-file-only-when-opening-it
(setq epa-file-cache-passphrase-for-symmetric-encryption t)
;; No more needing to enter passphrase at each save ^_^
;;
;; Caches passphrase for the current emacs session?
The purpose of encrypting a file is so that an adversary —e.g., an immoral computer administrator or a thief who stole your computer— will have to spend so much decrypting the data than the data is actually worth. As such, one uses GPG keys…!
Hydras let us do “super temporary modal editing” |
Sometimes we have keybindings that share a common prefix, say C-c j
and C-c k
,
and we invoke them in an arbitrary sequence, it would be nice to invoke the
shared prefix only once thereby having:
C-c j C-c j C-c k C-c k M-3 C-c j M-5 C-c k | ≈ | C-c jjkk3j5k |
- The “hydra-zoom” example from the documentation really showcases this utility.
- After the prefix is supplied, all extensions are shown in a minibuffer.
;; Invoke all possible key extensions having a common prefix by
;; supplying the prefix only once.
(use-package hydra)
From the Hydra repository is a ‘description for poets’:
Once you summon the Hydra through the prefixed binding (the body + any one head), all heads can be called in succession with only a short extension.
The Hydra is vanquished once Hercules, any binding that isn’t the Hydra’s head, arrives. Note that Hercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper command. This makes the Hydra very seamless, it’s like a minor mode that disables itself auto-magically.
⇒ The Hydra Wiki has many example hydras for common uses cases ⇐
Below are two examples; one to simplify textual navigation and another for
window navigation.
Yet another possible hydra would be to avoid remembering word operations, such
as copying a word, upcasing it, killing a word from anywhere within it —in
contrast kill-word
kills to the end of the word—, etc. Likewise for line
operations, such as copying a line from anywhere in it. See Taking a tour of
one’s edits below for another small and useful example.
When there are multiple actions, it’s nice to see such a menu displayed in the middle of the frame; so we use hydra-posframe. Moreover, it can be useful to group related actions under a common heading —e.g., textual navigation may occur at the line level or word level or screen level— we obtain a nice interface by declaraing hydras using pretty-hydra-define —this saves us the trouble of formating docstrings using classic hydra.
;; Show hydras overlayed in the middle of the frame
(use-package hydra-posframe
:disabled "TODO Fix me, breaking Github Actions test setup"
:quelpa (hydra-posframe :fetcher git :url
"https://github.com/Ladicle/hydra-posframe.git")
:hook (after-init . hydra-posframe-mode)
:custom (hydra-posframe-border-width 5))
;; Neato doc strings for hydras
(use-package pretty-hydra)
To actually define hydras, we make a helper function: doc:my/defhydra —which combines doc:defhydra and doc:pretty-hydra-define.
Basic movement commands —without having to hold the control key!
C-n
moves us to the next line, as it should; but it now also let’s us press
n,p,f,…
for other movement commands. Unlisted keys insert themselves, whereas q
close the pop-up menu.
(my/defhydra "C-n" "\t\t\t\t\tTextual Navigation" arrows
:Line
("n" next-line)
("p" previous-line)
("a" beginning-of-line)
("e" move-end-of-line)
("g" goto-line)
:Word
("f" forward-word "Next")
("b" backward-word "Previous")
("{" org-backward-element "Next Element")
("}" org-forward-element "Previous Element")
:Screen
("v" scroll-up-command "Scroll Down")
("V" scroll-down-command "Scroll Up")
("l" recenter-top-bottom "Center Page")
("r" move-to-window-line-top-bottom "Relocate Point")
("m" helm-imenu "Textual Menu"))
Along with the “pop-up window”, this is a useful way to (re)learn about Emacs’ features.
For “key-based navigation”, consider ‘EVIL-mode’ or ‘Spacemacs’, or ace-jump-mode (below).
Also …
;; C-n, next line, inserts newlines when at the end of the buffer
(setq next-line-add-newlines t)
;; Use ijkl to denote ↑←↓→ arrows.
(my/defhydra "C-c w" "\t\tWindow Adjustment" windows
:Both
("b" balance-windows "balance")
("s" switch-window-then-swap-buffer "swap")
:Vertical_adjustment
("h" enlarge-window "heighten")
("l" shrink-window "lower")
:Horizontal_adjustment
("n" shrink-window-horizontally "narrow")
("w" enlarge-window-horizontally "widen"))
;; Provides a *visual* way to choose a window to switch to.
;; (use-package switch-window )
;; :bind (("C-x o" . switch-window)
;; ("C-x w" . switch-window-then-swap-buffer))
;; Have a thick ruler between vertical windows
(window-divider-mode)
Let’s save a few precious seconds,
;; change all prompts to y or n
(fset 'yes-or-no-p 'y-or-n-p)
;; Make RETURN key act the same way as “y” key for “y-or-n” prompts.
;; E.g., (y-or-n-p "Happy?") accepts RETURN as “yes”.
(define-key y-or-n-p-map [return] 'act)
;; Enable all ‘possibly confusing commands’ such as helpful but
;; initially-worrisome “narrow-to-region”, C-x n n.
(setq-default disabled-command-function nil)
Pop up a terminal, do some work, then close it using the same command.
(use-package vterm) ;; Shell with a nearly universal compatibility with terminal applications 💝
;; "Intelligent" switching to vterm; eg creates it if it's not open, non-intrusive windowing, saves window setup, etc.
(use-package vterm-toggle
:bind* ("C-t" . vterm-toggle))
Shell-pop uses only one key action to work: If the buffer exists, and we’re in
it, then hide it; else jump to it; otherwise create it if it doesn’t exit. Use
universal arguments, e.g., C-u 5 C-t
, to have multiple shells and the same
universal arguments to pop those shells up, but C-t
to pop them away.
(use-package shell-pop
:custom
;; This binding toggles popping up a shell, or moving cursour to the shell pop-up.
(shell-pop-universal-key "C-t")
;; Percentage for shell-buffer window size.
(shell-pop-window-size 30)
;; Position of the popped buffer: top, bottom, left, right, full.
(shell-pop-window-position "bottom")
;; Please use an awesome shell.
(shell-pop-term-shell "/bin/zsh"))
Now that we have access to quick pop-up for a shell, let’s get a pretty and practical shell: zsh along with the Oh My Zsh community configurations give us:
brew install zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
This installs everything ^_^
;; Be default, Emacs please use zsh
;; E.g., M-x shell
(unless noninteractive (setq shell-file-name "/bin/zsh"))
Out of the box, zsh comes with
- git support; the left side indicates which branch we’re on and whether the repo is dirty, ✗.
- Recursive path expansion; e.g.,
/u/lo/b TAB
expands to/usr/local/bin/
- Over 250+ Plugins and 125+ Themes that are enabled by simply
mentioning their name in the
.zshrc
file.
The defaults have been good enough for me, for now —as all else is achieved via Emacs ;-)
Also, there’s the tldr tool which aims to be like terse manuals for
commandline-tools in the style of practical example uses cases: tldr 𝒳
yields a
number of ways you’d actually use 𝒳. ( In Emacs, kbd:C-t tldr 𝒳 ⟨return⟩. )
(system-packages-ensure "tldr")
4.13 hr: A horizontal for your terminal
When working in the terminal, at least in my day job, it can be helpful to
visually segregate large chunks of output. With the hr
command, I can run hr
'output5`
, for example, to have the same the string output5 repeated horizontal
across one line of my terminal. We can also just run hr
which is the same as hr
'#'
. Finally, you can also run hr '-#-' '-' '-#-'
to have 3 horizontal lines and
more generally hr p₁ p₂ … pₙ
will produce n-many horizontal lines with the
$ith$-line having pattern pᵢ
.
(system-packages-ensure "hr") ;; ≈ brew install hr
Since I’m using use-package
, I can invoke M-x describe-personal-keybindings
to see what key bindings I’ve defined.
Since not all my bindings are via use-package
, it does not yet cover all of my bindings.
We could run C-h b
to see all our bindings —or M-x describe-personal-bindings
to see only those set throughout our init file.
(use-package helm-descbinds
:bind ("C-h b" . helm-descbinds))
Finally, we can observe which features are active in our current Emacs with,
(message "Features: %s" features)
;; When we split open a new window, we usually want to jump to the new window.
(advice-add #'split-window-below :after (lambda (&rest _) (other-window 1)))
(advice-add #'split-window-right :after (lambda (&rest _) (other-window 1)))
C-r
in a terminal gets you to search shell history, here’s how to do
it from Emacs in a more interactive fashion.
;; Usage: M-x helm-shell-history
(use-package helm-shell-history
:config
(setq helm-shell-history-file "~/.zsh_history")
(bind-key "M-r" #'helm-shell-history shell-mode-map))
;; MacOS's default ⌘-SPC does not let us do either of the following scenarios:
;; Usage: M-x helm-osx-app RET preferences bat RET ⇒ See battery preferences settings
;; Another Usage: M-x helm-osx-app RET ⇒ See all apps, maybe we forgot about one of them from an install a long time ago, and open it
;; See https://www.alfredapp.com/ as an alternative (for non-Emacs users), which can do more.
(use-package helm-osx-app)
;; For non-MacOS, we can use [[https://github.com/d12frosted/counsel-osx-app][counsel-osx-app]], whose name is misleading.
Org-mode lets us run chunks of code anywhere, then feed their outputs to other chunks of code in possibly different programming languages: Org is a meta-(programming language).
Importantly, this means we can write text and whenever we need the result of some computation, we can place it there and then and only request its result appear in PDF/HTML export. The result is a single document.
( There is the org-modern package, which provides a modern look-and-feel: It makes Org look less like a markup and more like a word editor. Nice stuff. )
(setq org-use-speed-commands t)
This enables the Org Speed Keys so that when the cursor is at the beginning of a headline, we can perform fast manipulation & navigation using the standard Emacs movement controls, such as:
- kbd:# toggle
COMMENT
-ing for an org-header. - kbd:s toggles “narrowing” to a subtree; i.e., hide the rest of the document.
If you narrow to a subtree then any export, kbd:C-c_C-e, will joyously only consider the narrowed detail.
- kbd:u for going to upwards to parent heading
- kbd:i insert a new same-level heading below current heading.
- kbd:c for cycling structure below current heading, or
C
for cycling global structure. - kbd:w refile current heading; options list pops-up to select which heading to move
it to. Neato!
- kbd:g to go to another heading, without refiling anything.
;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful. ;; When I'm refiling my TODOS, then give me all the freedom. (setq org-refile-targets '((nil :maxlevel . 1) (org-agenda-files :maxlevel . 9))) ;; Maybe I want to refile into a new heading; confirm with me. (setq org-refile-allow-creating-parent-nodes 'confirm) ;; Use full outline paths for refile targets ;; When refiling, using Helm, show me the hierarchy paths (setq org-outline-path-complete-in-steps nil) (setq org-refile-use-outline-path 'file-path)
- kbd:g to go to another heading, without refiling anything.
- kbd:n/p for next/previous visible heading.
- kbd:f/b for jumping forward/backward to the next/previous same-level heading.
- kbd:D/U move a heading down/up.
- kbd:L/R recursively promote (move leftwards) or demote (more rightwards) a heading.
- kbd:I/O clock In/Out to the task defined by the current heading.
- Keep track of your work times!
- kbd:v view agenda.
- kbd:t/,/:/e to add a TODO state, priority level, tag, or effort estimate
- kbd:1/2/3 to mark a heading with priority, highest to lowest.
- kbd:^ sort children of current subtree; brings up a list of sorting options.
- kbd:k/@/a to kill or mark or archive the current subtree
- kbd:o to open a link mentioned in the subtree then go to the link; a pop-up of links appears.
We can add our own speed keys by altering the doc:org-speed-commands association list variable; e.g.,
;; TODO FIXME Crashes upon startup.
(when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property)))
;; Use ‘:’ and ‘e’ to set tags and effort, respectively.
⇒ Moreover, kbd:? to see a complete list of keys available. ⇐ |
The “super key” —aka the command or windows key— can be used to jump to the previous, next, or toggle editing org-mode source blocks.
;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill.
;;
(require 'org)
(use-package emacs
:bind (:map org-mode-map
("s-p" . org-babel-previous-src-block)
("s-n" . org-babel-next-src-block)
("s-e" . org-edit-special)
:map org-src-mode-map
("s-e" . org-edit-src-exit)))
Note that we could have bound kbd:⌘+e to doc:org-edit-src-code / doc:org-edit-src-exit, but instead chose the more general doc:org-edit-special since, well, look at the tooltip documentation: This allows us to use kbd:⌘+e to ‘e’dit all kinds of Org entities —including footnotes and export blocks. ( Footnotes can be quickly produced with doc:org-footnote-new. )
- kbd:C-⟨return⟩ , kbd:C-S-⟨return⟩ make a new heading where the latter marks it as a
TODO
. - By default kbd:M-⟨return⟩ makes it easy to work with existing list items, headings, tables, etc by creating a new item, heading, etc.
Usually we want a newline then we indent, let’s make that the default.
(add-hook 'org-mode-hook '(lambda ()
(local-set-key (kbd "<return>") 'org-return-indent))
(local-set-key (kbd "C-M-<return>") 'electric-indent-just-newline))
Notice that I’ve also added another kind of return, for when I want to break-out of the indentation approach and start working at the beginning of the line.
In summary:
key | method | behaviour |
---|---|---|
kbd:⟨return⟩ | doc:org-return-indent | Newline with indentation |
kbd:M-⟨return⟩ | doc:org-meta-return | Newline with new org item |
kbd:C-M-⟨return⟩ | doc:electric-indent-just-newline | Newline, cursor at start |
kbd:C-⟨return⟩ | doc:org-insert-heading-respect-content | New heading after current content |
kbd:C-S-⟨return⟩ | doc:org-insert-todo-heading-respect-content | Ditto, but with a TODO marker |
For example, to execute a shell command in Emacs, write a src
with a shell
command, then C-c c-c
to see the results. Emacs will generally query you to
ensure you’re confident about executing the (possibly dangerous) code block;
let’s stop that:
;; Seamless use of babel: No confirmation upon execution.
;; Downside: Could accidentally evaluate harmful code.
(setq org-confirm-babel-evaluate nil)
;; Never evaluate code blocks upon export and replace results when evaluation does occur.
;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’.
(setq org-babel-default-header-args
'((:results . "replace")
(:session . "none")
(:exports . "both")
(:cache . "no")
(:noweb . "no")
(:hlines . "no")
(:tangle . "no")
(:eval . "never-export")))
Some initial languages we want org-babel to support:
(defvar my/programming-languages
'(emacs-lisp shell python haskell
;; rust ;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust
ruby ocaml dot latex org js css
sqlite C) ;; Captial “C” gives access to C, C++, D
"List of languages I have used in Org-mode, for literate programming.")
;; Load all the languagues
;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust
(ignore-errors (cl-loop for lang in my/programming-languages
do (require (intern (format "ob-%s" lang)))))
;;
(org-babel-do-load-languages
'org-babel-load-languages
(--map (cons it t) my/programming-languages))
;; Preserve my indentation for source code during export.
(setq org-src-preserve-indentation t)
;; The export process hangs Emacs, let's avoid this.
;; MA: For one reason or another, this crashes more than I'd like.
;; (setq org-export-in-background t)
More languages can be added using doc:add-to-list.
Sometimes my Org-files contain configurations that are local to the file,
so I name all such src
blocks #+name: startup-code
and place # -*- eval: (my/execute-startup-blocks) -*-
at the top of the file so that such
blocks are evaluated when the file opens up.
- The
-*- ... -*-
notation is for making local configurations. - Use
M-x add-file-local-variable-prop-line
to have them inserted interactively.
(defun my/execute-startup-blocks ()
"Execute all startup blocks, those named ‘startup-code’.
I could not use ORG-BABEL-GOTO-NAMED-SRC-BLOCK since it only goes
to the first source block with the given name, whereas I'd like to
visit all blocks with such a name."
(interactive)
(save-excursion
(goto-char 0)
(while (ignore-errors (re-search-forward "^\\#\\+name: startup-code"))
(org-babel-execute-src-block))))
The following setup enables this feature in a safe fashion —e.g., we do not want to avoid evaluating a random person’s potentially dangerous code when we only want to look at it.
;; Please ask me on a file by file basis whether its local variables are ‘safe’
;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried
;; again for the same file.
(setq enable-local-variables t)
I have been using a combination of (org-babel-goto-named-src-block ⋯)
in
multi-line local-variable declarations —=M-x add-file-local-variable-prop=—
for a while in many files using a dedicated * footer :noexport:
section, but
this new approach frees from having such sections and instead to having a single
line at the top of the file. Moreover, being at the top of the file, such a line
is a nice green:‘in your face’ reminder that there is local configuration that
should have been loaded.
;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”.
;;
(font-lock-add-keywords 'org-mode
'(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)"
(1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾"))
(2 '(face (:foreground "blue")))
(3 '(face (:inherit (bold) :foreground "gray65") display "﴿"))
)))
;;
;; Let's do this for all my languages:
;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”.
(cl-loop for lang in my/programming-languages
do (font-lock-add-keywords 'org-mode
`(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang)
(1 '(face (:inherit (bold) :foreground "gray65") display "﴾"))
(2 '(face (:foreground "blue")))
(3 '(face (:inherit (bold) :foreground "gray65") display "﴿"))
))))
;;
(defun my/toggle-line-fontification ()
"Toggle the fontification of the current line"
(interactive)
(defvar my/toggle-fontify/current-line -1)
(defvar my/toggle-fontify/on? nil)
(add-to-list 'font-lock-extra-managed-props 'display)
(let ((start (line-beginning-position)) (end (line-end-position)))
(cond
;; Are we toggling the current line?
((= (line-number-at-pos) my/toggle-fontify/current-line)
(if my/toggle-fontify/on?
(font-lock-fontify-region start end)
(font-lock-unfontify-region start end))
(setq my/toggle-fontify/on? (not my/toggle-fontify/on?)))
;; Nope, we've moved on to another line.
(:otherwise
(setq my/toggle-fontify/current-line (line-number-at-pos)
my/toggle-fontify/on? :yes_please_fontify)
(font-lock-unfontify-region start end)))))
;; TODO FIXME; maybe ignore: Wasted too much time here already.
;; (add-hook 'post-command-hook #'my/toggle-line-fontification nil t)
;; (font-lock-add-keywords nil '((my/toggle-line-fontification)) t)
(setq org-fold-core-style 'overlays)
tldr: Add :Disabled:
to a heading to render its contents in a <details>
HTML
element. If a heading has a :COLOR
property, then that is used for the colour of
the element, defaulting to pink
.
(defmacro org-deftag (name args docstring &rest body)
"Re-render an Org section in any way you like, by tagging the section with NAME.
That is to say, we essentially treat tags as functions that act on Org headings:
We redefine Org sections for the same purposes as Org special blocks.
The “arguments” to the function-tag can be declared as Org properties, then
the function can access them using the `o-properties' keyword as in
(-let [(&plist :file :date :color) o-properties]
(insert \"%s: %s\" file date))
Anyhow:
ARGS are the sequence of items seperated by underscores after the NAME of the new tag.
BODY is a form that may anaphorically mention:
- O-BACKEND: The backend we are exporting to, such as `latex' or `html'.
- O-HEADING: The string denoting the title of the tagged section heading.
- O-PROPERTIES: A plist of the Org properties at point.
DOCSTRING is mandatory; everything should be documented for future maintainability.
The result of this anaphoric macro is a symbolic function name `org-deftag/NAME',
which is added to `org-export-before-parsing-hook'.
----------------------------------------------------------------------
Below is the motivating reason for inventing this macro. It is used:
** Interesting, but low-priority, content :details_red:
Blah blah blah blah blah blah blah blah blah blah blah.
Blah blah blah blah blah blah blah blah blah blah blah.
Here is the actual implementation:
(org-deftag details (color)
\"HTML export a heading as if it were a <details> block; COLOR is an optional
argument indicating the background colour of the resulting block.\"
(insert \"\n#+html:\"
(format \"<details style=\\\"background-color: %s\\\">\" color)
\"<summary>\" (s-replace-regexp \"^\** \" \"\" o-heading) \"</summary>\")
(org-next-visible-heading 1)
(insert \"#+html: </details>\"))
"
(let ((func-name (intern (format "org-deftag/%s" name))))
`(progn
(cl-defun ,func-name (o-backend)
,docstring
(outline-show-all)
(org-map-entries
(lambda ()
(-let [(&alist ,@ (mapcar #'symbol-name args)) (map-apply (lambda (k v) (cons (downcase k) v)) (org-entry-properties (point)))]
;; MA: Maybe get rid of o-heading and o-properties and let people operate on raw Org secitons
;; as they do with org-agenda. That might provide a more unified approach.
(let ((o-properties (map-into (map-apply (lambda (k v) (cons (intern (concat ":" (downcase k))) v)) (org-entry-properties (point))) 'plist))
(o-heading (progn (kill-line) (car kill-ring))))
(if (not (s-contains? (format ":%s" (quote ,name)) o-heading 'ignoring-case))
(insert o-heading)
(setq o-heading (s-replace-regexp (format ":%s[^:]*:" (quote ,name)) "" o-heading))
,@body)
;; Otherwise we impede on the auto-inserted “* footer :ignore:”
(insert "\n"))))))
(add-hook 'org-export-before-parsing-hook (quote ,func-name))
)))
;; MA: This is new stuff.
(put 'org-deflink 'lisp-indent-function 'defun)
(put 'org-deftag 'lisp-indent-function 'defun)
;; Example use
(org-deftag identity ()
"Do nothing to Org headings"
(insert o-heading)) ;; Wait, I think this strips tags?
(org-deftag disabled (color)
"Render the body of a heading in a <details> element, titled “Disabled”.
The heading remains in view, and so appears in the TOC."
(insert "\n") (insert o-heading) (insert "\n")
(insert "\n#+html:"
(format "<div> <details class=\"float-child\" style=\"background-color: %s\">"
(or color "pink"))
"<summary> <strong> <font face=\"Courier\" size=\"3\" color=\"green\">"
"Details ﴾This is disabled, I'm not actively using it.﴿"
"</font> </strong> </summary>")
;; Something to consider: (org-set-property "UNNUMBERED" "nil")
(org-next-visible-heading 1)
(insert "#+html: </details> </div>"))
M-<
to take us to the actual start of a buffer;
instead it’d be preferable to go to the first “semantic unit” in the buffer. For
example, when directory editing with dired
we should jump to the first file,
with version control with magit
we should jump to the first section, when
composing mail we should jump to the first body line, and in the agenda we
should jump to the first entry.
;; M-< and M-> jump to first and final semantic units.
;; If pressed twice, they go to physical first and last positions.
(use-package beginend
:config (beginend-global-mode))
(bind-key "C-c C-h"
(defun my/org-fold-current-subtree-anywhere-in-it ()
(interactive)
(save-excursion (save-restriction
(org-narrow-to-subtree)
(org-shifttab)
(widen))))
org-mode-map)
a | b |
---|---|
1 | 2 |
(quelpa '(org-pretty-table
:repo "Fuco1/org-pretty-table"
:fetcher github))
(add-hook 'org-mode-hook 'org-pretty-table-mode)
Being an ‘on the fly replacement mechanism’, we get that “C-u 80 -” also results in one smooth horizontal rule and vertical sequences of ‘|’ results in a smooth vertical line.
NOTE. This is comparable to org-modern-mode
’s pretty table rendering.
;; Move current word ←/→, or current line ↑/↓.
;; Todo: Compare with org-metaup and org-metadown...
(use-package drag-stuff
:config (cl-loop for (key . action) in '(("<M-down>" . drag-stuff-down)
("<M-up>" . drag-stuff-up)
("<M-right>" . drag-stuff-right)
("<M-left>" . drag-stuff-left))
do (bind-key key action org-mode-map))
(drag-stuff-global-mode 1))
Ruins Org-mode’s M-↑/↓ for moving entire sections around.
- I’d much rather have my new buffers in org-mode than fundamental-mode:
(setq-default major-mode 'org-mode)
;; E.g., in ELisp mode, the following is clickable and looks nice: [[info:man][Read the docs!]]
;;
;; In particular, when I tangle my init.org into a Lisp file, init.el, it has Org links
;; back to the original source section in Org, which I can then click to jump to, quickly.
;;
(use-package orglink
:config
(global-orglink-mode)
;; Only enable this in Emacs Lisp mode, for now.
(setq orglink-activate-in-modes '(emacs-lisp-mode)))
;; Ignore all header arguments relating to “:eval”. Do not evaluate code when I export to HTML or LaTeX or anything else.
(setq org-export-use-babel nil)
See The Communicative Value of Using Git Well
Note: Emacs has built-in version control, e.g., kbd:C-x_v_l to see the change log of the repository containing the current file.
doc:undo-tree-visualize, kbd:C-x_u, gives a visual representation of the current buffer’s edit history.<<undo-tree-setup>>
( We’re just showing the <<undo-tree-setup>>
from earlier since this is a good
place for such a setup. More importantly, we are not copy-pasting the setup: It
is written only once; in a single source of truth! )
;; By default C-z is suspend-frame, i.e., minimise, which I seldom use.
(global-set-key (kbd "C-z")
(lambda () (interactive)
(undo-tree-mode) ;; Ensure the mode is on
(undo-tree-visualize)))
By default, Emacs saves backup files —those ending in ~
— in the current
directory, thereby cluttering it up. Let’s place them in ~~/.emacs.d/backups~, in
case we need to look for a backup; moreover, let’s keep old versions since
there’s disk space to go around —what am I going to do with 500gigs when nearly
all my ‘software’ is textfiles interpreted within Emacs 😼
;; New location for backups.
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
;; Silently delete execess backup versions
(setq delete-old-versions t)
;; Only keep the last 1000 backups of a file.
(setq kept-old-versions 1000)
;; Even version controlled files get to be backed up.
(setq vc-make-backup-files t)
;; Use version numbers for backup files.
(setq version-control t)
Why backups? Sometimes I may forget to submit a file, or edit, to my version control system, and it’d be nice to be able to see a local automatic backup. Whenever ‘I need space,’ then I simply empty the backup directory, if ever. That the backups are numbered is so sweet ^_^
Like package installations, my backups are not kept in any version control system, like git; only locally.
Finally, let’s not create .#
files nor bother confirming killing processes.
(setq confirm-kill-processes nil
create-lockfiles nil)
Let’s use an elementary diff system for backups: backup-walker
essentially makes
all our backups behave as if they were (implicitly) version controlled.
(use-package backup-walker
:commands backup-walker-start)
In a buffer that corresponds to a file, invoke doc:backup-walker-start to see a
visual diff of changes between versions; then n
and p
to move between diffs. By
default, you see the changes ‘backwards’: Red means delete these things to get
to the older version; i.e., the red ‘-’ are newer items.
There is also doc:diff-backup for comparing a file with its backup.
#
Emacs only makes a backup the very first time a buffer is saved; I’d prefer Emacs makes backups everytime I save! —If I saved, that means I’m at an important checkpoint, so please check what I have so far as a backup!
;; Make Emacs backup everytime I save
(defun my/force-backup-of-buffer ()
"Lie to Emacs, telling it the curent buffer has yet to be backed up."
(setq buffer-backed-up nil))
(add-hook 'before-save-hook 'my/force-backup-of-buffer)
;; [Default settings]
;; Autosave when idle for 30sec or 300 input events performed
(setq auto-save-timeout 30
auto-save-interval 300)
It is intestesting to note that the above snippet could be modified to make our own backup system, were Emacs lacked one, by having our function simply save copies of our file —on each save— where the filename is augmented with a timestamp.
;; Move to OS’ trash can when deleting stuff
;; instead of deleting things outright!
(setq delete-by-moving-to-trash t
trash-directory "~/.Trash/")
Let’s setup an Emacs ‘porcelain’ interface to git —it makes working with version control tremendously convenient.
(Personal reminder: If using 2FA [two factor authentication], then when you do
git operations, such as git push
, you must use your PAT [personal access token]
instead of your password! Also: Install refined-github: Browser extension that
simplifies the GitHub interface and adds useful features!)
;; Bottom of Emacs will show what branch you're on
;; and whether the local file is modified or not.
(use-package magit
:bind (("C-c M-g" . magit-file-dispatch))
:config (global-set-key (kbd "C-x g") 'magit-status)
:custom ;; Do not ask about this variable when cloning.
(magit-clone-set-remote.pushDefault t))
Why use magit
as the interface to the git version control system? In a magit
buffer nearly everything can be acted upon: Press return
, or space
, to see
details and tab
to see children items, usually.
- kbd:C-x_g, doc:magit-status, gives you a nice buffer with an overview of the Git repo that you’re buffer is currently visiting.
- kbd:C-c_M-g, doc:magit-file-dispatch, lets you invoke Git actions on the
current file directly; e.g., following up with kbd:blame, kbd:log, kbd:diff,
kbd:stage, or kbd:commit the current file.
For ease, above, we have also bound this to kbd:C-c_g —reminiscent of kbd:C-x_g 😄
;; When we invoke magit-status, show green/red the altered lines, with extra ;; green/red on the subparts of a line that got alerted. (system-packages-ensure "git-delta") (use-package magit-delta :hook (magit-mode . magit-delta-mode)) ;; Don't forget to copy/paste the delta config into the global ~/.gitconfig file. ;; Copy/paste this: https://github.com/dandavison/delta#get-started
- Blame, doc:magit-blame, is super nice: The buffer gets annotations for each chunk
of text, regarding who authoured it, when, and their commit title.
Then kbd:q to quit the blame.
Likewise, doc:magit-log-buffer-file is super neat!
(progn (eshell-command "echo change-here >> ~/dotfiles/.emacs") (find-file "~/dotfiles/.emacs"))First, let’s setup our git credentials.
;; Only set these creds up if there is no Git email set up ---ie at work I have an email set up, so don't
;; override it with my personal creds.
;;
;; See here for a short & useful tutorial:
;; https://alvinalexander.com/git/git-show-change-username-email-address
(when (equal "" (shell-command-to-string "git config user.email "))
(shell-command (format "git config --global user.name \"%s\"" user-full-name))
(shell-command (format "git config --global user.email \"%s\"" user-mail-address)))
If we ever need to use Git in the terminal, it should be done with Emacs as the underlying editor
;; We want to reuse an existing Emacs process from the command line
;; E.g., emacsclient --eval '(+ 1 2)' # ⇒ 3
(server-start)
;; Or use it whenever we are editing a git message from the terminal
(shell-command "git config --global core.editor 'emacsclient -t -a=\\\"\\\"'")
This will start a daemon if there is not already one running —The -a
option—
and opens a new Emacs frame on the current terminal —The -t
option.
(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!
Below are the git repos I’d like to clone —along with a function to do so quickly.
;; Clone git repo from clipboard
(cl-defun maybe-clone (remote &optional local)
"Clone a REMOTE repository [from clipboard] if the LOCAL directory does not exist.
If called interactively, clone URL in clipboard into ~/Downloads then open in dired.
Yields ‘repo-already-exists’ when no cloning transpires, otherwise yields ‘cloned-repo’.
LOCAL is optional and defaults to the base name; e.g.,
if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y."
(interactive "P")
(when (interactive-p)
(setq remote (substring-no-properties (current-kill 0)))
(cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" remote) nil "No URL in clipboard"))
(unless local
(setq local (concat "~/" (if (interactive-p) "Downloads/" "") (file-name-base remote))))
;; (require 'magit-repos) ;; Gets us the magit-repository-directories variable.
;; (add-to-list 'magit-repository-directories `(,local . 0))
(if (file-directory-p local)
'repo-already-exists
(shell-command (concat "git clone " remote " " local))
(dired local)
'cloned-repo))
(maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d")
(maybe-clone "https://github.com/alhassy/alhassy.github.io" "~/blog")
;; (maybe-clone "https://github.com/alhassy/holy-books")
This doc:maybe-clone utility has genuinely
made it easier for me to learn about new projects and codebases from Github:
I type it in with the repo’s address, then kbd:C-x_C-e —doc:eval-last-sexp—
and then I can view it in my beloved Emacs (─‿‿─)
.
Moreover, this handy tool makes it so that you can list your Git repositories with doc:magit-list-repositories: It marks modified repos as “red:dirty”.
It may be useful to know that (magit-anything-modified-p t file)
can be used to
check if file
has been modified (doc:magit-anything-modified-p), whereas
(magit-status repo)
checks the status of a repository (doc:magit-status).
Finally, one of the main points for using version control is to have access to historic versions of a file. The following utility allows us to kbd:M-x_git-timemachine on a file and use kbd:p/n/g/q to look at previous, next, goto arbitrary historic versions, or quit.
(use-package git-timemachine )
If we want to roll back to a previous version, we just doc:write-file or kbd:C-x_C-s as usual! The power of text!
doc:vc-annotate is also very useful to go through history and work out when things went wrong.
;; Jump to a (ma)git repository with C-u C-x g.
;;
;; To get a selection of repositories (that have been visited at least once),
;; call with “C-u M-x magit-status” or “C-u C-x g”; use “C-u C-u C-x g” to
;; manually enter a path to a repository.
;;
;; We use projectile's record of known projects, and keep only projects with
;; .git directory.
(with-eval-after-load 'projectile
(setq magit-repository-directories
(thread-last (projectile-relevant-known-projects)
(--filter (unless (file-remote-p it)
(file-directory-p (concat it "/.git/"))))
(--map (list (substring it 0 -1) 0)))))
;; Follow-up utility
(defun my/update-repos ()
"Update (git checkout main & pull) recently visited repositories."
(interactive)
(cl-loop for (repo _depth) in magit-repository-directories
;; Is it “main” or “master”
for trunk = (s-trim (shell-command-to-string (format "cd %s; git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'" repo)))
do (message (format "🤖 %s ∷ Checking out & pulling main" repo))
(shell-command (format "cd %s; git checkout %s; git pull" repo trunk)))
(message "🥳 Happy coding!"))
Add faces to Magit to achieve icon and colored commit leaders. I also integrate Helm to prompt a leader when committing so there’s no need to remember or type out completely every leader we choose.
- It’s not just aesthetics. It’s about visual clarity.
- Here is an alternate approach: Add icons based on words mentioned in commit titles —no leaders required.
My personal choices for leaders are:
(pretty-magit "Add" ?➕ (:foreground "#375E97" :height 1.2) "✅ Create a capability e.g. feature, test, dependency.")
(pretty-magit "Delete" ?❌ (:foreground "#375E97" :height 1.2) "❌ Remove a capability e.g. feature, test, dependency.")
(pretty-magit "Fix" ?🔨 (:foreground "#FB6542" :height 1.2) "🐛 Fix an issue e.g. bug, typo, accident, misstatement.")
(pretty-magit "Clean" ?🧹 (:foreground "#FFBB00" :height 1.2) "✂ Refactor code; reformat say by altering whitespace; refactor performance.")
(pretty-magit "Document" ?📚 (:foreground "#3F681C" :height 1.2) "ℹ Refactor of documentation, e.g. help files.")
(pretty-magit "Feature" ?⛲ (:foreground "slate gray" :height 1.2) "⛳ 🇮🇶🇨🇦 A milestone commit - flagpost")
(pretty-magit "Generate" ?🔭 (:foreground "slate gray" :height 1.2) "Export PDF/HTML or tangle raw code from a literate program") ;; Generating artefacts
(pretty-magit "master" ? (:box t :height 1.2) "" t)
(pretty-magit "origin" ?🐙 (:box t :height 1.2) "" t)
;; Commit leader examples: https://news.ycombinator.com/item?id=13889155.
;;
;; Cut ~ Remove a capability e.g. feature, test, dependency.
;; Bump ~ Increase the version of something e.g. dependency.
;; Make ~ Change the build process, or tooling, or infra.
;; Start ~ Begin doing something; e.g. create a feature flag.
;; Stop ~ End doing something; e.g. remove a feature flag.
Sometimes it’s nice to flag a chunk of text by its author, such as ‘ MA ’ for ‘M’usa ‘A’l-hassy, or ‘ HACK ’ for text that needs to be improved. Such flags stand out from other text by being coloured and bold.
;; NOTE that the highlighting works even in comments.
(use-package hl-todo
;; I want todo-words highlighted in prose, not just in code fragements.
:hook (org-mode . hl-todo-mode)
:config
;; Adding new keywords
(cl-loop for kw in '("TEST" "MA" "WK" "JC")
do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3")))
;; Enable it everywhere.
(global-hl-todo-mode))
We’ve added few to the default flag keywords so that in total we have the following flags —where any sequence of at least 3 XXX are considered flags.
(JC WK MA TEST HOLD TODO NEXT THEM PROG OKAY DONT FAIL DONE NOTE KLUDGE HACK TEMP FIXME XXX+)
(defun add-watchwords () "Add TODO: words to font-lock keywords."
(font-lock-add-keywords nil
'(("\\(\\<TODO\\|\\<FIXME\\|\\<HACK\\|@.+\\):" 1
font-lock-warning-face t))))
(add-hook 'prog-mode-hook #'add-watchwords)
Lest these get buried in mountains of text, let’s have them become mentioned in
a magit status buffer —which uses the keywords from hl-todo
.
;; MA: The todo keywords work in code too!
(use-package magit-todos
:after magit
:after hl-todo
;; :hook (org-mode . magit-todos-mode)
:config
;; For some reason cannot use :custom with this package.
(custom-set-variables
'(magit-todos-keywords (list "TODO" "FIXME" "MA" "WK" "JC")))
;; Ignore TODOs mentioned in exported HTML files; they're duplicated from org src.
(setq magit-todos-exclude-globs '("*.html"))
(magit-todos-mode))
- Note that such TODO keywords are not propagated from sections that are purple:COMMENT-ed out in org-mode.
- Ensure you exclude generated files, such as the Emacs backups directory, from
being consulted. Using
magit
, press kbd:i to mark items to be ignored. - This feature also works outside of git repos.
Open a Magit status buffer, or run doc:magit-todos-list to show a dedicated to-do list buffer. You can then peek at items with space, or jump to them with enter.
#
Quickly & automatically glimpse who, why, and when a line or code block was changed, using blamer.el. Jump back through history to gain further insights as to how and why the code evolved with kbd:C-x_g_l_l (doc:magit-log-head) or doc:git-timemachine.(unless noninteractive
(use-package blamer
:quelpa (blamer :fetcher github :repo "artawower/blamer.el")
:custom
(blamer-idle-time 0.3)
(blamer-min-offset 70)
(blamer-max-commit-message-length 80) ;; Show me a lot of the commit title
:custom-face
(blamer-face ((t :foreground "#7a88cf"
:background nil
:height 140
:italic t)))
:config
(global-blamer-mode 1)))
This is so nice!. I’ve enabled it once and then it just “works in the background, silently”.
When reading a line and wondering “who changed/wrote this and why”, blamer.el answers that seamlessly —almost as if a transient comment 😁
- The “why” is answered by commit messages; which may lead to improved messages: “Hey, I was just browsing through the code, and landed on this commit, but the message does not tell me why the change was introduced. Would you please write more detailed commit messages.”
- The “when” gives a useful context in time about the change. For example, when looking for a regression/recent-bug, we can immediately see “Did this line even change in the past week? No? That must not be it then” and we move on.
If we want to see commit messages alongside all code, then we can invoke doc:magit-blame-addition or doc:vc-annotate. If we want to “walk-along changes” then we use doc:git-timemachine.
Let’s have, in a fringe, an indicator for altered regions in a version controlled file. The symbols “+, =” appear in a fringe by default for alterations —we may change these if we like.
;; Hunk navigation and commiting.
(use-package git-gutter :config (global-git-gutter-mode))
;; Diff updates happen in real time according when user is idle.
Let’s set a hydra so we can press C-x v n n p n
to move the next two
altered hunks, move back one, then move to the next. This saves me having
to supply the prefix C-x v
each time I navigate among my alterations.
At any point we may also press u 𝕩
to denote C-u ⟪prefix⟫ 𝕩
.
(defhydra hydra-version-control (global-map "C-x v")
"Version control"
;; Syntax: (extension method description)
("n" git-gutter:next-hunk "Next hunk")
("p" git-gutter:previous-hunk "Previous hunk")
("d" git-gutter:popup-hunk "Show hunk diff")
("r" git-gutter:revert-hunk "Revert hunk\n")
("c" git-gutter:stage-hunk "Stage hunk")
("s" git-gutter:statistic "How many added & deleted lines"))
Committing with C-x v c
let’s us use C-c C-k
to cancel and C-c C-c
to
submit the given message; C-c C-a
to amend the previous commit.
Alternatively, we may use diff-hl:
;; Colour fringe to indicate alterations.
;; (use-package diff-hl)
;; (global-diff-hl-mode)
;; Popup for who's to blame for alterations.
(use-package git-messenger
:custom ;; Always show who authored the commit and when.
(git-messenger:show-detail t)
;; Message menu let's us use magit diff to see the commit change.
(git-messenger:use-magit-popup t))
;; View current file in browser on github.
;; More generic is “browse-at-remote”.
(use-package github-browse-file )
;; Add these to the version control hydra.
;;
(defhydra hydra-version-control (global-map "C-x v")
("b" git-messenger:popup-message "Who's to blame?")
;; C-u C-x b ╱ u b ∷ Also show who authored the change and when.
("g" github-browse-file-blame "Show file in browser in github")
("s" magit-status "Git status of current buffer"))
Perhaps C-x v b
will motivate smaller, frequent, commits.
Obtaining URL links to the current location of a file —URLs are added to the kill ring. Usefully, if git-timemachine-mode is active, the generated link points to the version of the file being visited.
(use-package git-link )
(defhydra hydra-version-control (global-map "C-x v")
("l" git-link "Git URL for current location"))
Read here for more about version control in general.
Disabled: I seldom work with SVN anymore. |
Let’s use git as an interface to subversion repositories so that we can continue
to use magit
as our version control interface. The utility to do so is called
git svn
—note git 𝒳
on a MacOS is the same as git-𝒳
on other systems.
(use-package magit-svn
:hook (magit-mode . magit-svn-mode))
Here’s an example. The following command checksout an SVN repo; afterwhich we may open a file
there and do M-x magit-status
to get the expected porcelain git interface ^_^
(async-shell-command "mkdir ~/2fa3; git svn clone --username alhassm https://websvn.mcmaster.ca/csse2fa3/2019-2020_Term2 ~/2fa3/")
In the magit buffer, we may now use the N
key which wraps the git svn
subcommands fetch, rebase, dcommit, branch, tag
. For example:
- 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.)
We can work with Github/Gitlab/etc from the comfort of Emacs Magit using the forge package.
- In particular, it adds two new “topics” to the Magit status page:
Issues
andPull Requests
.- We can tab on these to see their “posts” —the contributions to the conversations and new commits.
- Here is a list of actions that can be done on topics and posts.
(use-package forge
:after magit)
- Make a file
~/.authinfo
whose top-most line ismachine api.github.com login ⟪𝑼𝑺𝑬𝑹𝑵𝑨𝑴𝑬⟫^forge password ⟪𝑻𝑶𝑲𝑬𝑵⟫
where ⟪⋯⟫ should be replaced by your Github username and a token —which can be created from this page with permissions forrepo, user, read:org
checked-off.- For more detailed information, see this Forge manual page.
- Now open a Magit status buffer for a repository and run
M-x forge-pull
, after which you should be able to see all issues and pull requests.- Alternatively, press
N
to see the forge menu. - Press
N f f
to pull latest information from the repository.- Note how pulling data from a forge’s API works the same way as pulling Git data does; you do it explicitly when you want to see the work done by others.
- We can also “c”reate a new “p”ull request or a new “i”ssue with
N c p
orN c i
, respectively. - ~5 min video
- Alternatively, press
To review a pull request press tab on Pull Requests
then press b y
to checkout that branch locally.
Upon startup, we want to be greeted with a useful, yet unobtrusive, message
briefly detailing major system details. Moreover, the bottom-most area of the
screen should display battery life, data, & time. Likewise, we may have a casual
file explorer —primarily to show-off to newcomers, since great functionality
is found with M-x dired
—doc:dired.
;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★
;; https://github.com/emacsorphanage/org-bullets
(use-package org-bullets :hook (org-mode . org-bullets-mode))
(if (member "Apple Color Emoji" (font-family-list))
(set-fontset-font t 'unicode "Apple Color Emoji" nil 'prepend)
(message-box "Musa: Install the font!"))
;; E.g., Download font such as https://fonts.google.com/noto/specimen/Noto+Color+Emoji
;; Double-click on the ttf file then select “install” to have it installed on your system
;; (Note: Noto does not work on my personal machine.)
;; Render ASCII such as “ :-) ” as emoji 🙂.
(use-package emojify)
(setq emojify-display-style 'unicode) ;; unicode is the way to go!
(setq emojify-emoji-styles '(unicode))
(global-emojify-mode 1) ;; Will install missing images, if need be.
Let’s always welcome ourselves when Emacs begins with a helpful message. For example, which user account is running and what are the version numbers of our primary tools.
;; Silence the usual message: Get more info using the about page via C-h C-a.
(setq inhibit-startup-message t)
(defun display-startup-echo-area-message ()
"The message that is shown after ‘user-init-file’ is loaded."
(message
(concat "Welcome " user-full-name
"! Emacs " emacs-version
"; Org-mode " org-version
"; System " (symbol-name system-type)
"/" (system-name)
"; Time " (emacs-init-time))))
Now my startup message is,
Welcome Musa Al-hassy! Emacs 27.1; Org-mode 9.4.4; System darwin/Musas-MacBook-Air.local; Time 13.331914 seconds
(format "; Time %.3fs"
(float-time (time-subtract (current-time) before-init-time)))
Let’s change the Emacs frame to mention the name of the buffer in focus, as well as a nice ‘motto’:
;; Keep self motivated!
(setq frame-title-format '("" "%b - Living The Dream (•̀ᴗ•́)و"))
I almost always have Emacs open; I don’t need a dashboard, but would like to see my to-do list and my init file, side-by-side.
;; I have symlinks for various things, just follow them, do not ask me.
(setq vc-follow-symlinks t)
;; After my settings have been loaded, e.g., fancy priorities
;; and cosmetics, then open my notes files.
(add-hook 'emacs-startup-hook
(lambda ()
(-let [my-life.el (getenv "MY_LIFE_ELISP")]
(unless org-default-notes-file
(error "Add to .zshrc “ export MY_LIFE_ELISP=\"/full/path/to/my-life.el\" ”, then load my-life.el"))
(load-file my-life.el))))
There is the neat-looking emacs-dashboard package that provides an extensbile yet minimalist splash screen showing recent files, projects, and bookmarks.
The ‘modeline’ is a part near the bottom of Emacs that gives information about the current buffer, such as its file-type/‘major-mode’ and enabled extensions/‘minor-modes’. Let’s use the doom-modeline, which is a sleek & minimalistic, yet fancy setup with the following notable perks:
- Gives each buffer a nice icon in the modeline (denoting its major mode; e.g., Lisp/JavaScript/Org/etc each get a cool icon).
- Name of file becomes red when unsaved/modified.
- Nice version control icon, with branch name.
- Name of file is of the shape is shown as “project/file.ext”, when a project
is detected using
projectile.el
. - Flycheck error reporting is ugly by default, and one would consider using flycheck-status-emojis to make things look better in a simple modeline, but Doom-modeline gives a nice status indicators for Flycheck.
- Shows “+2” when the text scale is two above usual.
For fine-grained control on what/how things appear, there is doc:doom-modeline-def-modeline and doc:doom-modeline-set-modeline.
NOTE: We need to run M-x
doc:nerd-icons-install-fonts for doom-modeline to use pretty icons —then restart Emacs.
;; The modeline looks really nice with doom-themes, e.g., doom-solarised-light.
(use-package doom-modeline
:config (doom-modeline-mode))
;; Use minimal height so icons still fit; modeline gets slightly larger when
;; buffer is modified since the "save icon" shows up. Let's disable the icon.
;; Let's also essentially disable the hud bar, a sort of progress-bar on where we are in the buffer.
(setq doom-modeline-height 21)
(setq doom-modeline-buffer-state-icon nil)
(setq doom-modeline-hud t)
(setq doom-modeline-bar-width 1)
;; Show 3 Flycheck numbers: “red-error / yellow-warning / green-info”, which
;; we can click to see a listing.
;; If not for doom-modeline, we'd need to use flycheck-status-emoji.el.
(setq doom-modeline-checker-simple-format nil)
;; Don't display the buffer encoding, E.g., “UTF-8”.
(setq doom-modeline-buffer-encoding nil)
;; Inactive buffers' modeline is greyed out.
;; (let ((it "Source Code Pro Light" ))
;; (set-face-attribute 'mode-line nil :family it :height 100)
;; (set-face-attribute 'mode-line-inactive nil :family it :height 100))
;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list.
;; E.g.: (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max)))))
;; We likely want to add this locally, to hooks on major modes.
;; When using helm & info & default, mode line looks prettier.
(use-package spaceline
:custom (spaceline-buffer-encoding-abbrev-p nil)
;; Use an arrow to seperate modeline information
(powerline-default-separator 'arrow)
;; Show “line-number : column-number” in modeline.
(spaceline-line-column-p t)
;; Use two colours to indicate whether a buffer is modified or not.
(spaceline-highlight-face-func 'spaceline-highlight-face-modified)
:config (custom-set-faces '(spaceline-unmodified ((t (:foreground "black" :background "gold")))))
(custom-set-faces '(spaceline-modified ((t (:foreground "black" :background "cyan")))))
(require 'spaceline-config)
(spaceline-helm-mode)
(spaceline-info-mode)
(spaceline-emacs-theme))
(spaceline-toggle-buffer-size-off) ;; Not interested in how large a buffer is.
(spaceline-toggle-input-method-off) ;; Usually a “Π” symbol in the modeline, since I use Unicode input with Agda, denoting the current language environment, buffer coding system, and current input method.
(spaceline-toggle-buffer-encoding-abbrev-off) ;; buffer-encoding-abbrev: the line ending convention used in the current buffer (unix, dos or mac).
(spaceline-toggle-hud-off) ;; A tiny “progress bar”; shows the currently visible part of the buffer.
(spaceline-toggle-helm-buffer-id-off) ;; I don't need to see “Helm M-x” whenever I press “M-x” or other Helm-powered commands.
Other separators —of modeline information— that I’ve considered include
'brace
instead of an arrow, and 'contour, 'chamfer, 'wave, 'zigzag
which look
like browser tabs that are curved, boxed, wavy, or in the style of driftwood.
Enabled minor modes clutter up the modeline with their names, albeit some have useful status information shown. We can either selectively pick which names/status are shown using diminish.el, possibly forgetting which minor modes are enabled or we can use minions.el to “gather up” all enabled minor modes, and recently enabled ones, under a single menu which doom-modeline shows as a simple configurations gear icon. ⚙. ⚙️
(setq doom-modeline-minor-modes t)
(use-package minions
:init (minions-mode 1))
;; If not for doom-modeline, we'd need to use fancy-battery-mode.el.
(display-battery-mode +1)
;; Show date and time as well.
;; [Simple Approach]
;; (setq display-time-day-and-date t)
;; (display-time)
;; [More Controlled Approach: Set date&time format]
;; a ≈ weekday; b ≈ month; d ≈ numeric day, R ≈ 24hr:minute.
(setq display-time-format "%a %b %d ╱ %r") ;; E.g.,: Fri Mar 04 ╱ 03:42:08 pm
(setq display-time-interval 1) ;; Please update the time every second.
(display-time-mode)
But doc:display-time-mode shows me a bit more info that I actually don’t care for; so let’s disable those.
;; I don't need the system load average in the modeline.
(setq display-time-default-load-average nil)
(setq display-time-load-average nil)
Likewise, let’s have the modeline display column numbers, but not line numbers. Instead, let’s have line numbers on the side of the buffer; moreover let’s have a uniform width for displaying line numbers, rather than having the width grow as necessary.
;; ;; Do not show me line numbers, nor column numbers, in the modeline
(column-number-mode -1)
(line-number-mode -1)
;; Likewise, no need to show me “Top∣Mid∣Bot” in the modeline.
(setq-default mode-line-percent-position nil)
Line numbers are a conventionally expected part of a user interface, but I’ve realised that I seldom need to see them. I can still jump to a line number provided by a compilation error with kbd:M-g_g; and toggle line numbers on when I’m pair programming with doc:display-line-numbers-mode.
- In Emacs, there are buffer which exist and contain textual data, but to actually see them one requires a window. In the same vein, there are line numbers but I don’t need to always see them.
- If I need an indication of ‘progress’, the modeline contains a percentage of how far I am in a buffer.
;; (setq display-line-numbers-width-start t)
;; (global-display-line-numbers-mode t)
By default when multiple files sharing the same name are opened, say for comparison from different directories, their buffers are named uniquely by having the format “⟨file-name⟩ <𝓃>”, for numbers 𝓃. It’d be more helpful to have the buffer names reflect their location.
;; Note that ‘uniquify’ is builtin.
(require 'uniquify)
(setq uniquify-separator "/" ;; The separator in buffer names.
uniquify-buffer-name-style 'forward) ;; names/in/this/style
Note that this does not affect cloning buffers, kbd:C-x_4_c.
( A function f is injective precisely when it’s distinction-preserving; i.e., x ≠ y ≡ f x ≠ f y. We can tell whether two things are the same or not, by ‘zooming in’ on their particular property ‘f’, which may be easier to compare. E.g., object IDs, hashcodes, unique keys in database tables. )
( Why am I bringing this up? I like math and seldom get to use it; so why not! )
Emacs’ default theme leaves much to be desired: It does not look sleek and shiny, which usually leaves first-timers with a poor, shallow, impression of the system. Below we install a few themes that make Emacs look exquisite. We cycle between the chosen themes with kbd:C-c_t, doc:my/toggle-theme.
- The Doom Themes also look rather appealing.
- A showcase of many themes can be found here.
;; Infinite list of my commonly used themes.
(setq my/themes
(cl-loop for (package . theme-variants-I-like) in
;; I like theme doom-flatwhite <3 It feels “warm”.
;; (I found out thanks to C-u C-c t!)
'((doom-themes doom-flatwhite doom-snazzy doom-monokai-ristretto doom-laserwave doom-solarized-light doom-vibrant)
(solarized-theme solarized-gruvbox-dark solarized-gruvbox-light)
(stimmung-themes stimmung-themes-light stimmung-themes-dark)
(shanty-themes shanty-themes-light)
(apropospriate-theme apropospriate-light) ;; /super/ nice! Super “clean”, like writing on paper
(tao-theme tao-yang) ;; nice light theme.
(leuven-theme leuven-dark leuven) ;; Nice minimal variant
(material-theme material-light)
(moe-theme moe-light)
(organic-green-theme organic-green)
(tango-plus-theme tango-plus)
;; I like all 3 variants.
(minimal-theme minimal minimal-black minimal-light)
(espresso-theme espresso)
(emacs dichromacy)
(modus-themes modus-operandi-tinted))
do (package-install package)
append theme-variants-I-like))
(setcdr (last my/themes) my/themes)
kbd:C-c_t to toggle between the personal themes.
(cl-defun my/load-theme (&optional (new-theme (completing-read "Theme: " (custom-available-themes))))
"Disable all themes and load the given one ---read from user when called interactively."
(interactive)
(mapc #'disable-theme custom-enabled-themes)
(load-theme new-theme)
(message "Theme %s" new-theme))
(cl-defun my/toggle-theme (&optional (new-theme (pop my/themes)))
"Disable all themes and load NEW-THEME, which defaults from ‘my/themes’.
When a universal prefix is given, “C-u C-c t”, we load a random
theme from all possible themes. Nice way to learn about more
themes (•̀ᴗ•́)و"
(interactive)
(-let [theme (if current-prefix-arg
(nth (random (length (custom-available-themes)))
(custom-available-themes))
new-theme)]
(my/load-theme theme)))
(global-set-key "\C-c\ t" 'my/toggle-theme)
(my/toggle-theme)
Apparently, there’s already a package that accomplishes these goals and more: theme-looper. I may switch to it, but for now my simple function above is slightly informative, to me at least, about how themes work and it does what I want.
…Actually, the above learning adventure has made it easy to provide a similar setup for fonts 😁
Likewise, kbd:C-c_F, doc:my/toggle-font, to quickly change fonts (according to mood 😸). [I already use kbd:C-c_f, doc:my/org-mode-format, for the more likely operation of formatting text.]
In any Org file, type elisp:menu-set-font
; then you can click on this link to
get a nice font selection menu —this can be useful for your own ‘personal startup buffer’.
Let’s use the following theme and font, upon startup.
(unless noninteractive
;; Breaks Gerrit: (my/toggle-font "Roboto Mono Light 14")
(my/toggle-theme 'solarized-gruvbox-light))
Let’s have the entire line containing the cursour be slightly highlighted.
;; Make it very easy to see the line with the cursor.
(global-hl-line-mode t)
Moreover, we reduce the mental strain of locating the cursour when navigation happens: When we switch windows or scroll, for example, we get a wave of light near the cursor.
(use-package beacon
:config (setq beacon-color "#666600")
:hook ((org-mode text-mode) . beacon-mode))
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.
Enable flashing mode-line on errors. E.g., C-g
, or calling an unbound key
sequence, or misspelling a word.
;; (setq visible-bell 1) ;; On MacOS, this shows a caution symbol ^_^
;; The doom themes package comes with a function to make the mode line flash on error.
;; (use-package doom-themes)
;; (require 'doom-themes-ext-visual-bell)
;; (doom-themes-visual-bell-config)
A blinking cursor rushes me to type; let’s slow down. Recently I’m thinking that
a blinking cursours prompts me to continue upwards and onwards.
(blink-cursor-mode 1)
As a laptop user, screen space is important, so let’s remove rarely used visual items.
(unless noninteractive
(tool-bar-mode -1) ;; No large icons please
(scroll-bar-mode -1)) ;; No visual indicator please
;; (menu-bar-mode -1) ;; The Mac OS top pane has menu options
Highlight matching ‘parenthesis’ when near one of them.
(setq show-paren-delay 0)
(setq show-paren-style 'mixed)
(show-paren-mode)
Colour parens, and other delimiters, depending on their depth. Very useful for parens heavy languages like Lisp.
(use-package rainbow-delimiters
:hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode))
For example:
(blue (purple (forest (green (yellow (blue))))))
There is a powerful package called ‘smartparens’ for working with pair-able
characters, but I’ve found it to be too much for my uses. Instead I’ll utilise
the lightweight package electric
, which Emacs provides out of the box.
(electric-pair-mode 1)
It supports, by default, ACSII pairs {}, [], ()
and Unicode ‘’, “”, ⟪⟫, ⟨⟩
.
When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed and considered as pairs. Let’s disassociate them from both notions.
;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition.
(setq electric-pair-inhibit-predicate
(lambda (c)
(or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c))))
;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’.
(modify-syntax-entry ?< "w<")
(modify-syntax-entry ?> "w>")
;; Act as usual unless a ‘<’ or ‘>’ is encountered.
;; ( char-at is really “character at poisition”; C-h o! )
(setq rainbow-delimiters-pick-face-function
(lambda (depth match loc)
(unless (member (char-after loc) '(?< ?>))
(rainbow-delimiters-default-pick-face depth match loc))))
Adding Org-emphasise markers for pair completion —Disabled.
Let’s add the org-emphasises markers: If we select a word then press *
, it
becomes bold; likewise for /
for emphasise.
(setq electric-pair-pairs
'((?~ . ?~)
(?* . ?*)
(?/ . ?/)))
;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file.
;; Disable pairs when entering minibuffer
(add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0)))
;; Renable pairs when existing minibuffer
(add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1)))
I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous Org headers, for which the ‘completed closing pair’ must tiresomely be deleted.
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.
Let us render Org-mode’s #+begin_src
and #+end_src
less obtrusively by,
e.g., having the former render as a pencil marker ✎
and the latter as a
tombstone □
—reminiscent of Halmos’ QED end-of-proof marker.
(add-hook 'org-mode-hook #'rasmus/org-prettify-symbols)
(org-mode-restart)
His development relies on built-in prettify-symbols-mode, which
disguises strings in a buffer for the sake of readability or
aesthetics. Following the example in the documentation, C-h f
prettify-symbols-mode
, we can quickly approximate his efforts for
example
blocks as follows, however a main issue is that source blocks
have busybodied headers which his setup disguises as ‘≡’.
(global-prettify-symbols-mode)
(defvar my/prettify-alist nil
"Musa's personal prettifications.")
(cl-loop for pair in '(;; Example of how pairs like this to beautify org block delimiters
("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒
("#+end_example" . ?⇐) ;; ⇐
;; Actuall beautifications
("==" . ?≈) ("===" . ?≈) ;; ("=" . ?≔) ;; Programming specific prettifications
("i32" . ?ℤ) ("u32" . ?ℕ) ("f64" . ?ℝ) ;; Rust specific
("bool" . ?𝔹)
;; ("\"\"\"\n" . ?“) ("\"\"\"" . ?”)
("\"\"\"" . ?“)
("fn" . ?λ)
("<=" . ?≤) (">=" . ?≥)
("->" . ?→) ("-->". ?⟶) ;; threading operators
("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols
do (push pair my/prettify-alist))
;; Replace all Org [metadata]keywords with the “▷” symbol; e.g., “#+title: Hello” looks like “▷ Hello”.
(cl-loop for keyword in '(title author email date description options property startup export_file_name html_head fileimage filetags)
do (push (cons (format "#+%s:" keyword) ?▷) my/prettify-alist))
(cl-loop for hk in '(text-mode-hook prog-mode-hook org-mode-hook)
do (add-hook hk (lambda ()
(setq prettify-symbols-alist
(append my/prettify-alist prettify-symbols-alist)))))
(add-hook 'org-mode-hook (lambda () (push '("# " . (?🎶 (Br . Bl) ?\ )) prettify-symbols-alist)))
For more on prettify-symbols-mode, read the informative Prettifying LaTeX Buffers.
(<= (+ 1 1) (--> 2)) 1 = 2 == 3 ===
See “Mathematical Notation in Emacs” for how such prettifications can make verbose (Python) scripts much more readable by employing more economical disguises.
A nice sanity:
;; Un-disguise a symbol when cursour is inside it or at the right-edge of it.
(setq prettify-symbols-unprettify-at-point 'right-edge)
$e^x = ∑n = 0^∞ \frac{x^n}{n!}$
awkward
or
Let’s make some things prettier than they appear by default.
;; org-mode math is now highlighted ;-)
(setq org-highlight-latex-and-related '(latex))
;; Extra space between text and underline line
(setq x-underline-at-descent-line t)
;; Hide the *,=,/ markers
(setq org-hide-emphasis-markers t)
;; Let’s limit the width of images inlined in org buffers to 400px.
(setq org-image-actual-width 400)
;; Visually, I prefer to hide the markers of macros, so let’s do that:
;; {{{go(here)}}} is shown in Emacs as go(here)
(setq org-hide-macro-markers t)
;; On HTML exports, Org-mode tries to include a validation link for the exported HTML. Let’s disable that since I never use it.
;; (setq org-html-validation-link nil)
;; Musa: This is super annoying, in practice.
(setq org-pretty-entities nil) ;; Also makes subscripts (x_{sub script}) and superscripts (x^{super script}) appear in org in a WYSIWYG fashion.
;; to have \alpha, \to and others display as utf8
;; http://orgmode.org/manual/Special-symbols.html
;;
;; Be default, any consectuive string after “_” or “^” will be shown in WYSIWYG fashion; the following requires “^{⋯}” instead.
;; (setq org-use-sub-superscripts (quote {}))
Org pretty entities seems rather impressive —=M-x org-entities-help= to see all possibilities, or add your own. I’m already using the Agda input method, so I wont use Org’s —Agda’s gives me a tiny menu narrowing possibilities as I type. However, it does make subscripts (xsub script) and superscripts (xsuper script) appear in Org in a WYSIWYG fashion.
Automatically display emphasis markers and links when the cursor is on them.
(c.f. fragtog
below)
(use-package org-appear
:hook (org-mode . org-appear-mode)
:init (setq org-appear-autoemphasis t
org-appear-autolinks nil
org-appear-autosubmarkers nil))
The following is now disabled (yet again, as of Dec/31/2020) —it makes my system slower than I’d like.
;; Show inline images when loading a new Org file.
(setq org-startup-with-inline-images t)
;; Whenever a src block is run, redisplay images so they're up-to-date.
;; Very useful when using ‘ob-latex-as-png’, below.
(add-hook 'org-babel-after-execute-hook #'org-redisplay-inline-images)
;; Automatically convert LaTeX fragments to inline images.
(setq org-startup-with-latex-preview t)
Org mode supports inline image previews of LaTeX fragments; e.g.,
;; Automatically toggle LaTeX previews when cursour enters/leaves them
(use-package org-fragtog
:disabled t
:hook (org-mode . org-fragtog-mode))
doc:org-latex-preview, kbd:C-c_C-x_C-l, renders $e^{i \pi} + 1 = 0$
into a
really nice inline image: $ei π + 1 = 0$. It also works for LaTeX
environments —for personal environments, just (add-to-list
'org-latex-packages-alist "LaTeX definitions here")
.
[Disabled]
;; Make previews a bit larger
(setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5))
;; I use a lot of Unicode, so let's always include a unicode header.
(maybe-clone "https://armkeh.github.io/unicode-sty/")
(setq org-format-latex-header
(concat org-format-latex-header
"\n\\usepackage{\\string~\"/unicode-sty/unicode\"}"))
;;
;; Now this looks nice too!
;; $\substack{𝔹 \\ ↓ \\ 𝒜}$ and $\mathbb{B}$.
;; Always support unicode upon LaTeX export
;; No need to explicitly import armkeh's unicode-sty in each org file.
(add-to-list 'org-latex-packages-alist
"\n\\usepackage{\\string~\"/unicode-sty/unicode\"}")
This approach does not work well for forming diagrams; I’ve tried to make tikzcd
work this way and failed. Using ob-latex-as-png
as a substitute.
;; \step should be local to \begin{calc}⋯\end{calc}!
(add-to-list 'org-latex-packages-alist
"\\def\\BEGINstep{ \\{ }
\\def\\ENDstep{ \\} }
\\newcommand{\\step}[2][=]{ \\\\ #1 \\;\\; & \\qquad \\color{maroon}{\\BEGINstep \\text{ #2 } \\ENDstep} \\\\ & }
\\newenvironment{calc}{\\begin{align*} & }{\\end{align*}}")
; (pop org-latex-packages-alist)
;; See also org-format-latex-header
;; Support “latex-as-png” src blocks, which show LaTeX as PNGs
(use-package ob-latex-as-png :disabled t)
Use ref:my-stuff
to refer to an Org entity with #+name: my-stuff
; which must
have a #+caption: ⋯
as well. Example entities include tables and source
blocks; as well as figure blocks. For equation blocks, you must use a
\label{⋯}
directly.
;; Use the “#+name” the user provides, instead of generating label identifiers.
(setq org-latex-prefer-user-labels t)
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 " ▷ "))
⟨ C-x C-v
to open a file or directory in dired, using the current buffer. ⟩
As mentioned earlier, dired
is Emacs’ built-in directory editor; it’s opened
with C-x d
. Dired let’s us treat directories as textual objects! In dired,
press h
to see the many actions available. Here’s a few…
The dired-hacks family of packages lets us, say, get a dired buffer out of a shell incantation that lists files, or use dired to open files with external tools. Below we use three of its packages.
Pressing i
inserts a directory’s children under it, indented, in the current
buffer. Useful to see what’s there.
(use-package dired-subtree
:bind (:map dired-mode-map
("i" . dired-subtree-toggle)))
When directory 𝒳
has only one child 𝒴
, then in dired, instead of 𝒳
, show me 𝒳/𝒴
with 𝒳
greyed out.
(use-package dired-collapse
:hook (dired-mode . dired-collapse-mode))
Begin dired with certain entries grouped together, according to some filtering
requirement; and with “garbage” files not shown —i.e., those ending in
.aux, .out
, etc.
(use-package dired-filter
:hook (dired-mode . (lambda () (dired-filter-group-mode)
(dired-filter-by-garbage)))
:custom
(dired-garbage-files-regexp
"\\(?:\\.\\(?:aux\\|bak\\|dvi\\|log\\|orig\\|rej\\|toc\\|out\\)\\)\\'")
(dired-filter-group-saved-groups
'(("default"
("Org" (extension "org"))
("Executables" (exexutable))
("Directories" (directory))
("PDF" (extension "pdf"))
("LaTeX" (extension "tex" "bib"))
("Images" (extension "png"))
("Code" (extension "hs" "agda" "lagda"))
("Archives"(extension "zip" "rar" "gz" "bz2" "tar"))))))
By default C-x d
invokes dired
, but I prefer neotree
for file
management.
⟨ Edit: As a naive user, this is what I thought; yet a year later, I’ve almost never used neotree. ⟩
Useful navigational commands include
U
to go up a directory.C-c C-c
to change directory focus;C-C c
to type the directory out.?
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.
The *scratch*
buffer is a nice playground for temporary data or experiments.
However, by default its contents are not saved –which may be an issue if we have not relocated our playthings to their appropriate files. Whence let’s save & restore the scratch buffer by default.
(use-package persistent-scratch
;; In this mode, the usual save key saves to the underlying persistent file.
:bind (:map persistent-scratch-mode-map ("C-x C-s" . persistent-scratch-save)))
We might accidentally close this buffer, so we could utilise the following.
(defun scratch ()
"Recreate the scratch buffer, loading any persistent state."
(interactive)
(switch-to-buffer-other-window (get-buffer-create "*scratch*"))
(condition-case nil (persistent-scratch-restore) (insert initial-scratch-message))
(org-mode)
(persistent-scratch-mode)
(persistent-scratch-autosave-mode 1))
;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch*
;; Upon startup, close the default scratch buffer and open one as specfied above
(ignore-errors (kill-buffer "*scratch*") (scratch))
I use Org-mode often, so that’s how I want things to appear.
(setq initial-scratch-message (concat
"#+title: Persistent Scratch Buffer"
"\n#\n# Welcome! This’ a place for trying things out."
"\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n"))
I really like my Helm-supported C-x b
, but the visial appeal of a tab bar for Emacs
is interesting. Let’s try it out and see how long this lasts —it may be like Neotree:
Something cute to show to others, but not as fast as the keyboard.
(use-package awesome-tab
:quelpa (awesome-tab :fetcher git :url "https://github.com/manateelazycat/awesome-tab.git")
:config (awesome-tab-mode t))
;; Show me /all/ the tabs at once, in one group.
(defun awesome-tab-buffer-groups ()
(list (awesome-tab-get-group-name (current-buffer))))
It’s been less than three days and I’ve found this utility to be unhelpful, to me anyhow.
An alternative is centaur-tabs.
Let’s load the following package, which automatically resizes windows so that the window containing the cursor is the largest, according to the golden ratio. Consequently, the window we’re working with is nice and large yet the other windows are still readable.
(use-package golden-ratio
:init (golden-ratio-mode 1))
After some time this got a bit annoying and I’m no longer using this.
An alternative, also disabled:
;; An automatic window-resizing mechanism.
;; A “calmer” alternative to golden-ratio.
;; https://github.com/cyrus-and/zoom
(use-package zoom :config (zoom-mode t))
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.
When cursor sits on a URL/Image/File link, try to preview it in a tooltip.- Useful to quickly preview files and images.
- See also: https://github.com/jcs-elpa/preview-it
(quelpa '(preview-it :repo "jcs-elpa/preview-it" :fetcher github))
;; (global-preview-it-mode)
This also works nicely when I’m looking to open a file; e.g., C-x C-f
~/.emacs.d/.as TAB
to preview my .aspell.en.pws
(Emacs personal dictionary) file
in a tooltip.
Likewise, I’d like to preview line when executing the doc:goto-line / kbd:M-g_M-g command.
(quelpa '(goto-line-preview :repo "jcs-elpa/goto-line-preview" :fetcher github))
(global-set-key [remap goto-line] 'goto-line-preview)
📆 Calendar ✉️
- I have to copy-paste it to get it to fontify.
- Likewise, Org headings need to be modified (e.g. pressing `t` to make them todo) to get them fontified.
SVG tags mode let’s us replase arbitrary regular expressions with beautiful SVG images that can be clicked to produce an action, and may have a tooltip to provide contextual information. Essentially an alternative to the built-in doc:font-lock-mode, which performs arbitrary syntax highlighting.
- For more power, use the
svg-lib
package. - The docs have nice examples. Here are more useful examples.
(use-package svg-tag-mode
:hook (org-mode prog-mode)
;; :config (global-svg-tag-mode) ;; Nope: Breaks xwidget-webkit-browse-url, issue#28.
:config
(cl-defun my/svg-tag-declare-badge (template face &optional tooltip-message-upon-hover)
;; Example faces: 'org-level-1 'org-todo 'font-lock-doc-face
"Given a TEMPLATE of the shape \"𝑿❙𝒀\", make SVG badge whose tag is 𝑿 and label is 𝒀.
When `svg-tags-mode' is enabled, every occurence of \"\\(𝑿\\)\\(𝒀\\)\"
is replaced by an SVG image essentially displaying “[𝑿∣𝒀]” using the given FACE.
This badge can be clicked to show all instances in the buffer.
You can see the badges documentation / intentions / help-message when you hover over it;
to see TOOLTIP-MESSAGE-UPON-HOVER.
Both 𝑿 and 𝒀 are regeular expressions; “❙” serves as the SVG tag-label delimiter
---i.e., it saves as from writing \"\\(𝑿\\)\\(𝒀\\)\". Moreover, the SVG is only active
when regexp \"\\(𝑿\\)\\(𝒀\\)\" matches an instance."
;; Append tooltip message with a notice on what happens upon click.
(--> "Click on me to all see occurrences of this badge, in the current buffer!"
(if tooltip-message-upon-hover (concat tooltip-message-upon-hover "\n\n" it) it)
(setq tooltip-message-upon-hover it))
(-let [(tag label) (s-split "❙" template)]
(-let [click-to-show-all-buffer-occurrences `(lambda () (interactive) (occur (concat ,tag ,label)))]
;; Make an SVG for the tag.
(push
(cons (format "\\(%s\\)%s" tag label) `((lambda (tag) (svg-tag-make (s-chop-suffix ":" (s-chop-prefixes '("[" "<" "/*") tag)) :face (quote ,face) :inverse t :margin 0 :crop-right t :crop-left nil))
,click-to-show-all-buffer-occurrences
,tooltip-message-upon-hover))
svg-tag-tags)
;; Make an SVG for the label.
(push
(cons (format "%s\\(%s\\)" tag label) `((lambda (label) (svg-tag-make (s-chop-suffixes '("]" ">" "*/") label) :face (quote ,face) :crop-left t))
,click-to-show-all-buffer-occurrences
,tooltip-message-upon-hover))
svg-tag-tags))))
;; Let's start off empty; then declare badges below.
(setq svg-tag-tags nil)
;; Using caps so that these stick-out somewhat even when svg-tags-mode is not present.
(my/svg-tag-declare-badge "TODO:❙.*" 'org-todo "This is something I would like to do, in the future.")
(my/svg-tag-declare-badge "SILLY:❙.*" 'error "I’m experimenting; don't forget to clean-up when you’re done!")
(my/svg-tag-declare-badge "HACK:❙.*" 'error "This works, but it’s far from ideal. Plan to clean this in the future.")
(my/svg-tag-declare-badge "FIXME:❙.*" 'org-todo "This is busted! Plan to fix this in the future.")
(my/svg-tag-declare-badge "NOTE:❙.*" 'org-done "Something to be aware of; to keep in mind.")
;; [In]Active Time stamps --- M-x org-time-stamp
(my/svg-tag-declare-badge "\\[2022-.* ❙.*]" 'org-done "This is an inactive time stamp. It does not trigger the parent entry to appear in the agenda.")
(my/svg-tag-declare-badge "<2022-.* ❙.*>" 'org-todo "This is an active time stamp. It causes the parent Org entry to appear in the agenda.")
;; JavaScript Lint Rules: \* eslint (.*) */
(my/svg-tag-declare-badge "/\\* eslint ❙.* \\*/" 'org-done "It looks like you’ deviating from common conventions: Tread cautiously!")
;; TODO: Make SVG tags for other interesting “2-part” pieces of textual information
)
;; If everything is setup, the following examples should look like SVGs.
;; NOTE: Do something
;; TODO: fix me later
;; HACK: hiya
;; FIXME: this thing is busted 🎭
;; SILLY: start
;; SILLY: end
;; [2022-04-20 Sun 16:30]
;; <2022-04-20 Sun 16:30>
;; /* eslint eqeqeq: 0, curly: 2 */
;; NOTE: Toggle svg-tags-mode; useful when experimenting with new tags.
;; (progn (svg-tag-mode-off) (svg-tag-mode-on))
;; NOTE: (my/toggle-line-fontification) works fine with svg-tag-mode :-)
(quelpa '(org-remoteimg :fetcher github :repo "gaoDean/org-remoteimg"))
(require 'org-remoteimg)
(setq url-cache-directory "~/emacs.d/.cache/")
(setq org-display-remote-inline-images 'cache)
Example: lexing vs parsing
;; Let's use an improved buffer list.
(use-package ibuffer ;; This is built-into Emacs.
:bind ("C-x C-b" . ibuffer))
;; It uses similar commands as does dired; e.g.,
;; / . org
;; This filters (“/”) the list with extensions (“.”) being “org”.
(use-package ibuffer-vc
:hook (ibuffer . (lambda ()
(ibuffer-vc-set-filter-groups-by-vc-root)
(unless (eq ibuffer-sorting-mode 'alphabetic)
(ibuffer-do-sort-by-alphabetic))))
:custom
(ibuffer-formats '((mark modified read-only " "
(name 18 18 :left :elide) " "
(size 9 -1 :right) " "
(mode 16 16 :left :elide) " "
(vc-status 16 16 :left) " "
(vc-relative-file)))))
- Tips for using Emacs Ibuffer
- (~10 minute video) Using Emacs - 34 - ibuffer and emmet - C’est la Z
- (~10 minute video) Emacs: introduction to IBUFFER - YouTube
7.24 [better than ibuffer!] A butler for your buffers. Group buffers into workspaces with programmable rules, and easily switch to and manipulate them.
(use-package bufler
:config (bind-key "C-x C-b" #'bufler-list))
;; I still prefer “C-x b” to be “helm-mini”, since when looking for a buffer it also shows me recently visited files.
(use-package all-the-icons
:config (all-the-icons-install-fonts 'install-without-asking))
I’ve loved using XMonad as a window tiling manager. I’ve enjoyed the ability to segregate my tasks according to what ‘project’ I’m working on; such as research, marking, Emacs play, etc. With perspective, I can do the same thing :-)
That is, I can have a million buffers, but only those that belong to a workspace will be visible when I’m switching between buffers, for example. ( The awesome-tab and centaur-tab, mentioned elsewhere here, can be used to achieve the same thing by ‘grouping buffers together’. )
(use-package perspective
:config ;; Activate it.
(persp-mode)
;; In the modeline, tell me which workspace I'm in.
(persp-turn-on-modestring))
All commands are prefixed by C-x x
; main commands:
s, n/→, p/←
- ‘S’elect a workspace to go to or create it, or go to ‘n’ext one, or go to ‘p’revious one.
c
- Query a perspective to kill.
r
- Rename a perspective.
A
- Add buffer to current perspective & remove it from all others.
As always, since we’ve installed which-key
, it suffices to press C-x x
then look
at the resulting menu 😃
(cl-defun my/clean-buffers ()
"Kill all buffers that are not associated with a file.
By convention, such files are named in *earmuffs* style."
(interactive)
(ignore-errors (mapcar #'kill-buffer (--filter (s-matches? "\\*.*\\*" it) (mapcar #'buffer-name (buffer-list))))))
- If I want to see a particular buffer, I’ll summon it explicitly.
- E.g., Messages with
C-h e
, Magit withC-x g
, Server Status withM-S-SPC
, etc.
- E.g., Messages with
(defun my/buffer-predicate (buffer)
"Run `C-u 0 C-x C-e' on the following form to see all buffer names and find the
ones annyoning you, then place those in the function body below
(mapcar #'buffer-name (buffer-list))
"
;; First let's kill a bunch of buffers
;; (my/clean-buffers) ;; TODO: Bad idea?
;; Next let's filter out any remaining ones [Redundant?]
(defvar my/ignore/buffer/name '("*Quail Completions*" "*Backtrace*" "*Help*" "*agda2*" "*sqls*" "*which-key*" "*Warnings*" "*Messages*" "Status of Services"))
(defvar my/ignore/buffer/prefix '("*helm" "*Helm" "*quelpa" "*lsp" "*Occur" "magit" "*Flymake" "*format" "*Shell" "*Async"
"*org-src-fontification:" "*Server:"))
(defvar my/ignore/buffer/suffix '("stderr*" "log*" "-ls*"))
(-let [name (buffer-name buffer)]
(not (or (member name my/ignore/buffer/name)
(--any? (s-starts-with? it name) my/ignore/buffer/prefix)
(--any? (s-ends-with? it name) my/ignore/buffer/suffix)))))
(set-frame-parameter nil 'buffer-predicate 'my/buffer-predicate)
Note: C-x C-r
is very useful for jumping to recently visited files.
Evaluate code and see the results inline —A feedback loop that is faster than ever! |
Within Emacs, kbd:C-x_C-e evaluates a Lisp expression anywhere; e.g.,
at the end of (message-box "hello world")
press C-x C-e
to see a greeting.
- We ran some code without explicitly running an interpreter/repl/compiler!
- This is known as “REPL driven development” (RDD):
There is a running REPL server for your language, implicitly in the
background, and your editor (say with
C-x C-e
) will send it a line (or a selected region) of code for evaluation; we then see the result as an overlay in our current buffer.- You choose which code gets (re)evaluated.
- A quick introduction to RDD can be viewed at https://alhassy.com/repl-driven-development, including setup for other languages.
By default, Emacs Lisp’s kbd:C-x_C-e shows results only in the minibuffer; near the bottom of the screen. Let’s also have evaluation results displayed as inline overlays —at the location that the user, us, is actually looking/working; rather than forcing their eyes to shift up&down when writing&evaluating.
- kbd:C-u_C-x_C-e inserts the evaluation result at point; kbd:C-u_0_C-x_C-e does so without truncating lengthy output.
- Read this Sweet & short blog/GIFs on practical uses of kbd:C-x_C-e when working with Lisp.
;; Evaluation Result OverlayS for Emacs Lisp
(use-package eros :init (eros-mode t))
(use-package repl-driven-development)
;; TODO: rdd.el should (require ⋯) the following!
(use-package json-navigator)
(use-package ansi-color)
(setq repl-driven-development-echo-duration 10)
;; Sometimes I see a bunch of shell incantations in a README or something and I'd like to execute them right there and then,
;; and not have to bother with copying them over to a terminal and execute there. As such, here's a quick key binding to execute
;; shell commands from anywhere.
;; (repl-driven-development [C-x C-t] "bash" :prompt "bash-3.2\\$")
(repl-driven-development [C-x C-t] terminal)
;; TODO: Changes to be pushed to master rdd.el ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; new
(defun repl-driven-development--main-callback (repl)
"Return the callback that works on REPL."
`(lambda (process output)
;; This is done to provide a richer, friendlier, interaction.
;; ^M at the end of line in Emacs is indicating a carriage return (\r)
;; followed by a line feed (\n).
(setq output (repl-driven-development--ignore-ansi-color-codes output)) ;; ⟵ ⟵ Main change here ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵
;; (message-box "%s" output)
(setq output (s-trim (s-replace-regexp ,(rdd@ repl prompt) ""
(s-replace "\r\n" "" output))))
;; Output is always non-empty
(unless (s-blank? (s-trim output))
(setf (rdd@ (quote ,repl) output) output))
(repl-driven-development--insert-or-echo (quote ,repl) output)))
;; Notice that I've changed :prompt!
(defun repl-driven-development-preconfiguration:java (keys)
"<omitted>"
(repl-driven-development
keys
;; enable assertions, and add everything installed, via `mvn', in scope.
(format "jshell --class-path %s --enable-preview -R -ea --feedback silent"
(concat ".:" (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f 2>/dev/null | tr '\n' ':'")))
:name 'java-eval
:prompt "[^\\n]*=>\\.\\.\\.\\." ;; ⟵ ⟵ Main change here ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵ ⟵
:input-rewrite-fn
#'repl-driven-development--strip-out-C-style-comments&newlines
:init "/set mode EmacsJavaMode normal -command
/set format EmacsJavaMode display \"{pre}added import {name}{post}\" import-added
/set format EmacsJavaMode display \"{pre}re-added import {name}{post}\" import-modified,replaced
/set format EmacsJavaMode result \"{type} {name} = {value}{post}\" added,modified,replaced-primary-ok
/set truncation EmacsJavaMode 40000
/set feedback EmacsJavaMode
import javax.swing.*;
System.out.println(\"Enjoy Java with Emacs (。◕‿◕。))\")")
(defalias 'java-eval-read #'repl-driven-development--java-read))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set “Cx Cj” to evaluate Java code in a background REPL.
(repl-driven-development [C-x C-j] java)
Edit comment/string/docstring/code block in separate buffer with your favourite mode.
- ⌘-e to toggle “e”diting of thing at point.
- ⌘-e will try to edit thing at point, or selection, if possible; otherwise it
will exit edit session.
- 🔥 Avoid escape nightmares by editing strings in a separate buffer.
- On a string, press ⌘-e and a new buffer pops up with unescaped content,
letting you edit raw strings directly. It then takes care of automatically
escaping strings for you when you press ⌘-e, or
C-c C-c
—or discard your changes withC-c C-k
.
- ⌘-e can be used within edit session to create new edit sessions, if possible; then ⌘-e will “pop-off-the-stack” as expected.
- (I already use ⌘-e to toggle editing Org src blocks, and when editing a block ⌘-e will try to do a recursive edit if possible, or exit the edit session.)
- (The separedit package also let’s me press ⌘-e to edit variable values when describing them, and to edit text when in the minibuffer.)
From source:
+----------+ Edit +-----------+ Edit +-----------+ | | ---------------------> | edit | ---------------------> | edit | ... | | point-at-comment? | buffer | point-at-comment? | buffer | | source | point-at-string? | | point-at-string? | | ... | buffer | point-at-codeblock? | (markdown | point-at-codeblock? | (markdown | ... | | point-at-...? | orgmode | point-at-...? | orgmode | | | <--------------------- | ...) | <--------------------- | ...) | ... +----------+ Commit changes +-----------+ Commit changes +-----------+
[In-general, we can invoke M-x edit-indirect-region to edit any selected piece
of text in its own buffer, then C-c C-c
(or ⌘-e
with my setup) to commit the
edit. Actually, I’ve altered by ⌘-e
setup to also account for a selected region
😉]
(use-package separedit)
;;
;; # Example Usage
;;
;; 1. Press ⌘-e on this line, to edit this entire comment.
;; 2. Press ⌘-e to exit the edit session.
;;
;; Since my ⌘-e is context sensitive, to determine whether to continue editing or
;; exit; you can explicitly request an edit with C-c ' and an exit with C-c C-c.
;;
;; ```
;; ;; 3. Press ⌘-e on this line, to edit this source block!
;; ;; 4. Press ⌘-e on this line, to edit this inner-most comment!
;; ;; 5. At start of next line, press “⌘-r ⌘-e” to edit just the source block
;; ;;
;; (cl-defun index (&rest args)
;; "6. Press ⌘-e to edit this string, \"7. and again in these quotes\""
;; "<p>8. Press ⌘-e to edit this <strong> HTML </strong> block, in Web-mode </p>")
;;
;; ;; 9. Press C-u ⌘-e to guess the language of the next string (Rust); then ⌘-r C-c C-r to quickly run the code.
;; "fn main() { println!(\"{}\", \"hello!\"); }"
;;
;; ;; 10. Select & press “C-u ⌘-e” on the following, to edit it in whatever mode you want.
;; ;; select * from table -- Or just press ⌘-e and have the mode detected.
;;
;; ```
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Setup to make the above ⌘-e behaviour happen.
;; Make “⌘-e” toggle editing string literals / select region / [Org/markdown] code block / comment block when programming.
(--map (bind-key "s-e" #'separedit it)
'(prog-mode-map minibuffer-local-map help-mode-map)) ;; TODO: helpful-mode-map
;; TODO:Merge these changes upstream
;; I'm focusing on a specific region to edit, so let's not be distracted by anything else.
;; This makes the “editing stack” feel like a stack, with ⌘-e pushing new editing session buffers,
;; and C-c C-c, or ⌘-e on non-editable lines, to pop-off the stack.
;; (advice-add #'separedit :after (lambda (&rest _) (delete-other-windows)))
;;
;; NOTE: This actually breaks the stack nature of popping with ⌘-e; we need to actually save the stack via some list of buffers than push/pop buffers on that variable.
;; I don't want to be bothered for what mode I'm in, when a region is selected using current major mode.
;; I'll use a prefix, “C-u ⌘-e”, if I want to select a mode for my current selected text.
(advice-add #'separedit--select-mode :before-until
(lambda (&rest _)
(when (and (not current-prefix-arg) (region-active-p)) (pp-to-string major-mode))))
;; Also: When on a string ∷
(advice-add #'separedit--select-mode :before-until
(lambda (&rest _)
"When on a string ∷
+ ⌘-e ⇒ Edit string at point
+ C-u ⌘-e ⇒ Auto-detect my string's major mode
+ C-u C-u ⌘-e ⇒ Let me select a major mode"
(-let [str? (ignore-errors (thing-at-point 'string))]
(case (car current-prefix-arg)
(4 (when str? (pp-to-string (my/detect-prog-mode str?))))
(_ nil)))))
;; NOTE: By default, separedit provides colouring for 'strings', "strings", and `strings'
;; This doesn't look very good when I have a single quote within double quotes:
;; In an Emacs Lisp buffer, editing the string "Bob's Work" gives unexpected highlighting.
;; ```
;; (advice-add #'separedit :after
;; (lambda (&rest _)
;; (when (s-ends-with? "string-mode" (pp-to-string major-mode))
;; (text-mode))))
;; ```
;; In the indirect buffer, make ⌘-e finish editing.
(use-package edit-indirect
:config (bind-key "s-e"
(lambda ()
(interactive)
(or (ignore-errors (call-interactively #'separedit))
(call-interactively #'edit-indirect-commit)))
#'edit-indirect-mode-map))
;; I also have “s-e” bound to `org-edit-src-exit'.
(advice-add 'org-edit-src-exit :before-until
(lambda (&rest r)
(when (ignore-errors (separedit)) t)))
;; → ⌘-e on an Org paragraph pops-up an edit session in Org mode.
;; → ⌘-e on a selection in Org mode pops-up an edit session in Org mode.
;; TODO: Consider forming an alist for special blocks to refer to their preferred
;; edit mode, defaulting to Org-mode? Perhaps something to consider /after/
;; addressing the bug below.
;; (advice-unadvice 'org-edit-special) MA: TODO: FIXME: Delete this?
(advice-add 'org-edit-special :around
(lambda (orginal-function &rest r)
(cond
((region-active-p) (call-interactively #'edit-indirect-region) (org-mode))
((equal 'paragraph (car (org-element-at-point)))
(mark-paragraph) (call-interactively #'edit-indirect-region) (org-mode))
(t (or (ignore-errors (apply orginal-function r))
;; We try to edit a special block when orginal-function fails.
;; This way src blocks are not confused with the more generic idea of special blocks.
(when
(my/org-in-any-block-p)
;; Note using org-element-at-point doesn't work well with special blocks when you're somewhere within the block.
;; It only works correctly when you're on the boundary of the special block; which is not ideal.
;; This is why I'm not using: (org-element-property :begin elem).
(-let [(start . end) (my/org-in-any-block-p)]
(set-mark-command start)
(goto-char end) (previous-line 2) (end-of-line) ;; FIXME: Still shows #+end_XXX for some reason.
(call-interactively #'edit-indirect-region) (org-mode))))))))
;; where...
(defun my/org-in-any-block-p ()
"Return non-nil if the point is in any Org block.
The Org block can be *any*: src, example, verse, etc., even any
Org Special block.
This function is heavily adapted from `org-between-regexps-p'.
Src: https://scripter.co/splitting-an-org-block-into-two/"
(save-match-data
(let ((pos (point))
(case-fold-search t)
(block-begin-re "^[[:blank:]]*#\\+begin_\\(?1:.+?\\)\\(?: .*\\)*$")
(limit-up (save-excursion (outline-previous-heading)))
(limit-down (save-excursion (outline-next-heading)))
beg end)
(save-excursion
;; Point is on a block when on BLOCK-BEGIN-RE or if
;; BLOCK-BEGIN-RE can be found before it...
(and (or (org-in-regexp block-begin-re)
(re-search-backward block-begin-re limit-up :noerror))
(setq beg (match-beginning 0))
;; ... and BLOCK-END-RE after it...
(let ((block-end-re (concat "^[[:blank:]]*#\\+end_"
(match-string-no-properties 1)
"\\( .*\\)*$")))
(goto-char (match-end 0))
(re-search-forward block-end-re limit-down :noerror))
(> (setq end (match-end 0)) pos)
;; ... without another BLOCK-BEGIN-RE in-between.
(goto-char (match-beginning 0))
(not (re-search-backward block-begin-re (1+ beg) :noerror))
;; Return value.
(cons beg end))))))
I’d like to guess the language I’m in, when working with strings.
(use-package language-detection)
;; Usage: M-x language-detection-buffer ⇒ Get programming language of current buffer
;; Also, (language-detection-string "select * from t") ;; ⇒ sql
;; TODO: Push this upstream; https://github.com/andreasjansson/language-detection.el/issues/1
(cl-defun my/detect-prog-mode (&optional string)
"Guess programming mode of the current buffer, or STRING if it is provided.
When called interactively, it enables the mode;
from Lisp it just returns the name of the associated mode.
;; Example Lisp usage
(call-interactively #'my/detect-prog-mode)
`language-detection-buffer' returns a string which is not always the name of the
associated major mode; that's what we aim to do here."
(interactive)
(defvar my/detect-prog-mode/special-names
'((c . c-mode)
(cpp . c++-mode)
(emacslisp . emacs-lisp-mode)
(html . web-mode) ;; I intentionally want to use this alternative.
(matlab . octave-mode)
(shell . shell-script-mode)
(visualbasic . visual-basic-mode)
(xml . sgml-mode))
"Names in this alist map a language to its mode; all other languages 𝒳 have mode ‘𝒳-mode’ afaik.")
(let* ((lang (if string (language-detection-string string) (language-detection-buffer)))
(mode (or (cdr (assoc lang my/detect-prog-mode/special-names))
(intern (format "%s-mode" lang)))))
(if (called-interactively-p 'any)
(progn (call-interactively mode) (message "%s enabled!" mode))
mode)))
- ⌘-e to edit an Org table cell; see here for built-in table editing commands.
(advice-add #'org-edit-special :before-until
(lambda (&rest r)
(when (equal 'table-row (car (org-element-at-point)))
(call-interactively #'org-table-edit-field))))
Super sleek way to select regions: Anywhere press kbd:⌘-r to select the current word, press it again to select sentence, then again for the current paragraph, then more to get the current section.
(use-package expand-region
:bind (("s-r" . #'er/expand-region)))
You can watch an introductory ~3 minute video to expand-region at Emacs Rocks!.
That is, repeated ⌘+r expands the selection to the next logical segment of text: In writing this means “Word, sentence, paragraph”, and in programming this means “identifier, then incrementally larger scopes”.
Using kbd:⌘-i and kbd:⌘-o we can quickly, for example, delete a string or its contents; or delete a {}-block or just its contents; or delete a ()-argument list or just its contents, etc.
change-inner
gives you vim’s ci
command:
change-inner {
⇒ Delete all text starting from the first ‘{’ delimiter to the next one; but keep the delimiters.change-outer {
⇒ As above, but also delete the delimiters.
(use-package change-inner
:bind (("s-i" . #'change-inner)
("s-o" . #'change-outer)))
Emacs is an extensible self-documenting editor!
Let’s use a helpful Emacs documentation system that cleanly shows a lot of
contextual information —then let’s extend that to work as we want it to:
C-h o
to describe the symbol at point.
(use-package helpful)
(defun my/describe-symbol (symbol)
"A “C-h o” replacement using “helpful”:
If there's a thing at point, offer that as default search item.
If a prefix is provided, i.e., “C-u C-h o” then the built-in
“describe-symbol” command is used.
⇨ Pretty docstrings, with links and highlighting.
⇨ Source code of symbol.
⇨ Callers of function symbol.
⇨ Key bindings for function symbol.
⇨ Aliases.
⇨ Options to enable tracing, dissable, and forget/unbind the symbol!
"
(interactive "p")
(let* ((thing (symbol-at-point))
(val (completing-read
(format "Describe symbol (default %s): " thing)
(vconcat (list thing) obarray)
(lambda (vv)
(cl-some (lambda (x) (funcall (nth 1 x) vv))
describe-symbol-backends))
t nil nil))
(it (intern val)))
(cond
(current-prefix-arg (funcall #'describe-symbol it))
((or (functionp it) (macrop it) (commandp it)) (helpful-callable it))
(t (helpful-symbol it)))))
;; Keybindings.
(global-set-key (kbd "C-h o") #'my/describe-symbol)
(global-set-key (kbd "C-h k") #'helpful-key)
I like helpful and wanted it to have the same behaviour as C-h o
, which
helpful-at-point
does not achieve. The incantation above makes C-h o
use helpful
in that if the cursor is on a symbol, then it is offered to the user as a
default search item for help, otherwise a plain search box for help
appears. Using a universal argument lets us drop to the built-in help command.
If you query C-h o mapcar
you get some nice docs, but it’d be even nice to get
some example usage of that method.
(use-package elisp-demos
:config
;; Show demos when I do a `C-h o'.
(advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update)
;; Show demos in tooltips when I pause to select a completion, in Emacs Lisp mode.
(advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1))
(defvar my/cheatsheet/cached-topics nil)
(cl-defun my/cheatsheet (&optional topic)
"Clone Al-hassy's ⟨TOPIC⟩CheatSheet repository when called from Lisp; visit the pretty HTML page when called interactively.
- Example usage: (my/cheatsheet \"Vue\")
- Example usage: M-x my/cheatsheet RET Vue RET."
(interactive)
(if (not topic)
(browse-url (format "https://alhassy.github.io/%sCheatSheet" (completing-read "Topic: " my/cheatsheet/cached-topics)))
(push topic my/cheatsheet/cached-topics)
(maybe-clone (format "https://github.com/alhassy/%sCheatSheet" topic))))
Let’s actually get some repos locally, and use: M-x my/cheatsheet
to view the pretty HTML (or PDF) sheets.
(mapcar #'my/cheatsheet '("ELisp" "GojuRyu" "Rust")) ; Python Prolog Vue Agda JavaScript
; Clojure Ruby Oz Coq Cats Haskell FSharp OCaml
When programming, sometimes you just gotta Google “how do I do ⋯”.
- The usual process is (1) open a browser, (2) make a Google query, (3) look at StackOverflow’s most upvoted answer for your query, (4) copy-paste the code solution/example to your editor; [(5) get distracted by interesting things you’d like to read].
- Better would be to use the howdoi tool, which gives instant coding answers for common questions via the command line.
- Below, my Emacs Lisp function doc : howdoi let’s me reduce the 4-step process
to just 2 steps: Write your query anywhere then call
M-x howdoi
on it to replace the query with the answer. (OrC-u M-x howdoi
to see the full answer and a link to it on StackOverflow.)⚡ Never open your browser to look for help again ⚡
(system-packages-ensure "howdoi")
(cl-defun howdoi (&optional show-full-answer)
"Instantly insert coding answers.
Replace a query with a code solution; replace it with an entire
answer if a prefix is provided.
Example usage:
On a new line, write a question such as:
search and replace buffer Emacs Lisp
Then invoke ‘M-x howdoi’ anywhere on the line
to get a code snippet; or ‘C-u M-x howdoi’ to get a full answer to your query.
"
(interactive "P")
(let ((query (s-collapse-whitespace (substring-no-properties (thing-at-point 'line))))
(flag (if show-full-answer "-a" "")))
(beginning-of-line)
(kill-line)
(insert (shell-command-to-string (format "howdoi %s %s" query flag)))))
In emacs-lisp-mode
we can enable eldoc-mode
—“Elisp Live Documentation”— to
display information about a function or a variable in the echo area.
(use-package eldoc
:hook (emacs-lisp-mode . turn-on-eldoc-mode))
;; Slightly shorten eldoc display delay.
(setq eldoc-idle-delay 0.4) ;; Default 0.5
Friendly reminder that M-.
takes you to the definition, and M-,
takes you back
to where you originally where.
Out-of-the-box Emacs has ‘xref’ utilities M-.
and C-u M-.
to Find Identifier
References; however, tags to source definitions need to be generated using the
etags
program. Nonetheless, the xref utilites are impressive and some just work:
For example, M-?
cleverly finds all references for an identifier in ‘near by’
files; whereas C-u M-. RET my/.*
, for example, uses the given regular expression
to list all identifiers with prefix my/
, thereby listing my personally defined
names ^_^
C-M-. 𝓇𝓮ℊ𝓮𝓍 | Find all identifiers whose name matches the given pattern |
Let’s get dumb-jump, where the ‘dumb’ is possibly due to the fact that it works by brute-force regular-expression lookup of pre-defined ‘definitional template’ rules. It “just works” ^_^
(use-package dumb-jump
:bind (("M-g q" . dumb-jump-quick-look) ;; Show me in a tooltip.
("M-g ." . dumb-jump-go-other-window)
("M-g b" . dumb-jump-back)
("M-g p" . dumb-jump-go-prompt)
("M-g a" . xref-find-apropos)) ;; aka C-M-.
:config
;; If source file is visible, just shift focus to it.
(setq dumb-jump-use-visible-window t))
In Lisp, for binding macros, it lists all possible mentions of the bound
variable —the first is likely what is desired. Alternatively, one could just
add the necessary rule to the variable dumb-jump-find-rules
. Otherwise, it
works fine even for locally bound definitions. It works depending on the
extension of a file.
Files in 3ʳᵈ-party directories should be read-only whenever I open them, when
I’m jumping to definitions (with M-.
).
;; Usage: Press “M-”. “use-package” below and you can accidentally alter the source code!
;; But in this case you likely just wanted to see the 3ʳᵈ-party definition, not alter it.
;; As such, with this advice, the source will not be alterable (unless you toggle read-only mode).
(advice-add #'xref-find-definitions :after
(lambda (&rest _)
(when (--map (s-ends-with? it (f-parent buffer-file-name))
'("lisp/emacs-lisp" "/lisp" ".emacs.d/elpa/"))
(read-only-mode))))
Let’s get some nifty commenting features —the link has nice usage gifs.
- M-;
repeatedly does (1) comments current line, (2) inserts a comment at the end
of the current line, and (3) deletes an existing end-of-line comment.
C-u_M-; indents the current enf-of-line comment with any above it.
For use with Org-mode, it’s best to use doc:org-edit-src-code —which I’ve bound to ⌘ e.
(use-package comment-dwim-2 :bind ("M-;" . comment-dwim-2)) ;; Not ideal: M-; comments a parent Org heading and not the current line. ;; (define-key org-mode-map (kbd "M-;") 'org-comment-dwim-2)
;; In VSCode, with the “Better Comments” extension, comments starting with a “bang” are made to stand out, via bold red.
;; Let's do the same thing in Emacs.
;; I did not look around, there might be a package/option for this 🤷
(add-hook 'prog-mode-hook
(defun emphasize-comments-starting-with-! ()
(highlight-lines-matching-regexp ".*\\*.*!.*" 'hi-red-b)
(highlight-lines-matching-regexp ".*//!.*" 'hi-red-b)
(highlight-lines-matching-regexp ";;!.*" 'hi-red-b))) ;;! Look it works (。◕‿◕。)
GIF: https://endlessparentheses.com/images/comment-box.gif
(defun my/comment-box (b e)
"Draw a box comment around the region but arrange for the region
to extend to at least the fill column. Place the point after the
comment box.
Source: http://irreal.org/blog/?p=374
To do fancy stuff like removing boxes, centering them, etc
see https://github.com/lewang/rebox2/blob/master/rebox2.el"
(interactive "r")
(let ((e (copy-marker e t)))
(goto-char b)
(end-of-line)
(insert-char ? (- fill-column (current-column)))
(comment-box b e 1)
(goto-char e)
(set-marker e nil)))
A comment box sometimes increases the size of a file more than I’d like, or more than others on my dev team would like. As such, let’s try banner comments:
- GIF: https://github.com/WJCFerguson/banner-comment/blob/35d3315683d3f97605207691b77e9f447af18fe2/demo.png
(use-package banner-comment)
Pretty slick!
⟦‼ TODO: hideshowvis shows "*hideshowvis*"
literally in src code blocks upon
export; as such we need to hook to exports so as to temporarily disable it. ⟧
(require 'cl-lib)
(defun my/disable-hs-hide-all (orig-fun &rest args)
"Advise `org-export-dispatch` to disable `hs-hide-all` temporarily."
;; Without this, export hangs “Hiding all blocks...”
(cl-letf (((symbol-function 'hs-hide-all) (lambda (&rest _) nil)))
;; Without this, export shows “*hideshowvis*” markers in my exported code blocks.
(cl-letf (((symbol-function 'hideshowvis-highlight-hs-regions-in-fringe) (lambda (&rest _) nil)))
(apply orig-fun args))))
(advice-add 'org-export-dispatch :around #'my/disable-hs-hide-all)
Literate programming within Org-mode is not always ideal or possible, so we use
a programming mode directly and then may want to have arbitrary ‘sections’ of
text folded up. Let’s describe how to accomplish this goal. I’ve tried the
feature-full folding modes Origami-mode and yafolding-mode
, but it seems the
built-in TODO. doc.hs-minor-mode is the best: (0) It folds comments, (1) it’s the
fastest, and (2) it Just Worksᵀᴹ out-of-the-box. Coupled with Vimish folding,
for arbitrary region folding; and a helpful hydra, I’m rather happy with my
setup.
On two JavaScript files, I found the following timings (using TODO. doc.elp-instrument-function and TODO. doc.elp-results):
Folding function | ~500 lines | ~2300 lines |
---|---|---|
hs-hide-all | 0.00277 | 0.004358 |
origami-close-all-nodes | 0.00304 | 0.025569 |
yafolding-hide-all | 7.051795 | 74.252598 |
(TODO: Maybe these timings are due to my config, I should try to reproduce these
with emacs -Q
.)
TODO: Below I setup hideshow along with [doc : hideshowvis-symbols] [hideshowvis] which gives us a nice clickable “+/-” marker for foldable blocks as well as an overlay that indicates how many lines are folded away. For more on hideshow-mode, see the Hidden Gem Packages presentation which contains a small self-contained introduction, or these super useful source comments which discuss hooks and support for special modes (like this one).
(use-package hideshow
:init
;; https://github.com/emacsmirror/emacswiki.org/blob/master/hideshowvis.el
(quelpa '(hideshowvis :fetcher wiki))
;; Press “C-c TAB” to toggle a block's visibility or “C-c f” for my folding hydra.
:bind (("C-c TAB" . hs-toggle-hiding))
;; https://github.com/shanecelis/hideshow-org/tree/master
;; This extension bring Org-mode tab behaviour to folding, at the block level
;; and buffer level ---but not cycling visibility.
;; (use-package hideshow-org) ;; Disabled as commented below.
:hook ((prog-mode . (lambda () (hs-minor-mode +1)
(hideshowvis-minor-mode t)
(hideshowvis-symbols)
;; This hook along with hs-org mode breaks editing of src blocks in Org files.
;; That's OK, since my folding hydra does a better job for my needs.
;; (hs-org/minor-mode t)
(hs-hide-all)))))
Along with a hydra for super quick navigation and easily folding, unfolding blocks! Love this one (•̀ᴗ•́)و
(my/defhydra "C-c f" "Folding text" archive
:Current
("h" hs-hide-block "Hide")
("s" hs-show-block "Show")
("t" hs-toggle-hiding "Toggle")
;; "l" hs-hide-level "Hide blocks n levels below this block"; TODO: Enable folding feature
:Buffer
("H" hs-hide-all "Hide")
("S" hs-show-all "Show")
("T" my/hs-toggle-buffer "Toggle")
:Style
("i" my/clever-selective-display "Fold along current indentation" :toggle selective-display)
("e" auto-set-selective-display-mode "Explore; walk and see" :toggle t)
:Region
("f" (lambda () (interactive) (vimish-fold-toggle) (vimish-fold (region-beginning) (region-end))) "Fold/Toggle")
("d" vimish-fold-delete "Delete fold")
("U" vimish-fold-unfold-all "Unfold all")
("D" vimish-fold-delete-all "Delete all")
("n" vimish-fold-next-fold "Next fold")
("p" vimish-fold-previous-fold "Previous fold")
:...
("w" hl-todo-occur "Show WIPs/TODOs" :exit t)
("m" lsp-ui-imenu "Menu of TLIs" :exit t) ;; TLI ≈ Top Level Items
;; ("i" imenu-list "iMenu (General)") ;; It seems the above is enough for both prog and otherwise.
("r" (progn (hs-minor-mode -1) (hs-minor-mode +1)) "Reset Hideshow") ;; Remove all folds from the buffer and reset all hideshow-mode. Useful if it messes up!
("q" nil "Quit" :color blue))
;; Features from origami/yafolding that maybe I'd like to implement include:
;; narrowing to block or folding everything except block, navigating back and forth between folded blocks.
;; Finally, if we want to cycle the visibility of a block (as in Org-mode), we can use a combination of hs-show-block and hs-hide-level.
(defun my/clever-selective-display (&optional level)
"Fold text indented same of more than the cursor.
This function toggles folding according to the level of
indentation at point. It's convenient not having to specify a
number nor move point to the desired column.
"
(interactive "P")
(if (eq selective-display (1+ (current-column)))
(set-selective-display 0)
(set-selective-display (or level (1+ (current-column))))))
;; Src: https://emacs.stackexchange.com/questions/52588/dynamically-hide-lines-indented-more-than-current-line
(define-minor-mode auto-set-selective-display-mode
"Automatically apply `set-selective-display' at all times based on current indentation."
nil "$" nil
(if auto-set-selective-display-mode
(add-hook 'post-command-hook #'auto-set-selective-display nil t)
(remove-hook 'post-command-hook #'auto-set-selective-display t)
(with-temp-message ""
(set-selective-display nil))))
;;
(defun auto-set-selective-display ()
"Apply `set-selective-display' such that current and next line are visible.
Scroll events are excluded in order to prevent wild flickering while navigating."
(unless (eq last-command #'mwheel-scroll)
(let*((this-line-indent (current-indentation))
(next-line-indent (save-excursion (forward-line) (current-indentation))))
(with-temp-message "" ; Suppress messages.
(set-selective-display (1+ (max this-line-indent next-line-indent)))))))
With expected support for searching.
;; Open folded nodes if a search stops there.
(add-hook 'helm-swoop-after-goto-line-action-hook #'my/search-hook-function)
(defun my/search-hook-function ()
(when hs-minor-mode (set-mark-command nil) (hs-show-block) (pop-to-mark-command)))
With expected support for searching.
(defun my/search-hook-function ()
(when origami-mode (origami-open-node-recursively (current-buffer) (point))))
;; Open folded nodes if a search stops there.
(add-hook 'helm-swoop-after-goto-line-action-hook #'my/search-hook-function)
;;
;; Likewise for incremental search, isearch, users.
;; (add-hook 'isearch-mode-end-hook #'my/search-hook-function)
And hydra…
(my/defhydra "C-c f" "Folding text" archive
:Current
("h" origami-close-node-recursively "Hide")
("s" origami-open-node-recursively "Show")
("c" origami-recursively-toggle-node "Cycle") ;; Acts like org-mode header collapsing. Cycle a fold between open, recursively open, closed.
("f" origami-show-only-node "Focus") ;; Close everything but the folds necessary to see the point. Very useful for concentrating on an area of code.
;; ("H" origami-close-all-nodes "Hide All")
;; ("S" origami-open-all-nodes "Show All")
:Navigate
("n" origami-next-fold "Next")
("p" origami-previous-fold "Previous")
:...
("t" origami-toggle-all-nodes "Toggle buffer")
("m" lsp-ui-imenu "Menu of TLIs" :exit t) ;; TLI ≈ Top Level Items
;; ("i" imenu-list "iMenu (General)") ;; It seems the above is enough for both prog and otherwise.
("r" origami-reset)) ;; Remove all folds from the buffer and reset all origami state. Useful if origami messes up!
With a single space or tab, my code should always remain indented.
aggressive-indent-mode is a minor mode that keeps your code always indented. It reindents after every change, making it more reliable than electric-indent-mode.
;; Always stay indented: Automatically have blocks reindented after every change.
(use-package aggressive-indent :config (global-aggressive-indent-mode t))
;; Use 4 spaces in places of tabs when indenting.
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
This mode requires my Lisp forms to be well-balanced; e.g., having a missing
parenthesis does not indent. For instance, I have a form F
and I write (when F
but have not yet enclosed the final parens, I still want nice indentation. So…
;; This minor mode toggles on along with elisp-mode and indents on the fly when
;; you edit the code. No special key strokes needed.
;; Demo: https://www.youtube.com/watch?v=zrFmfFZfj-A&ab_channel=JiahaoLi
(use-package el-fly-indent-mode
:config (add-hook 'emacs-lisp-mode-hook #'el-fly-indent-mode))
The following is also “OK” in Org-mode ;-)
;; Add a visual indent guide
(use-package highlight-indent-guides
:hook (prog-mode . highlight-indent-guides-mode)
:custom
(highlight-indent-guides-method 'character)
(highlight-indent-guides-character ?|)
(highlight-indent-guides-responsive 'stack))
(load-file "~/.emacs.d/elpa/highlight-indent-guides-20200820.2328/highlight-indent-guides.el")
Disabled: I’m not actively writing C-like code in Emacs. |
The following minor mode automatically adds spacing around operators.
(use-package electric-operator
:hook (c-mode . electric-operator-mode))
I dislike it when users write x=y+1
—whitespace is free and helpful. ⟨ Also,
languages with arbitrary identifiers, like Lisp and Agda, would accept x=y+1
as
an identifier, not an expression! ⟩
Disabled: I mainly code in ELisp and so don’t require this utility. |
Auto-format source code in many languages with one command
Let’s auto-format source code in many languages using the same command for all languages, instead of learning a different Emacs package and formatting command for each language.
Just do M-x format-all-buffer
and it will try its best to do the right thing. To
auto-format code on save, use the minor mode format-all-mode. Better yet, we ask
for it to do so “on save”.
- You will need to install external programs to do the formatting. If
format-all-buffer
can’t find the right program, it will try to tell you how to install it. - An alternative Emacs tool is apheleia: 🌷 Run code formatter on buffer contents without moving point, using RCS patches and dynamic programming.
(use-package format-all
;; To enable format on save for most programming language buffers:
:hook (prog-mode . format-all-mode)
:config
;; Please use the default formatters; I don't care too much.
(add-hook 'format-all-mode-hook 'format-all-ensure-formatter))
Prettier is an opinionated code formatter for numerous web related languages, including JS, TS, HTML, CSS, JSON, Vue, Markdown, and YAML.
;; For JavaScript prettification: It automatically inserts semicolons, forces newlines, inserts parens, etc.
;; Lots of redundant stuff, but stuff to make it easy to work with others.
(shell-command "npm install --global prettier")
;; Specific package to do only JS prettification: https://github.com/prettier/prettier-emacs
For example, in a NodeJS app make a file .prettierrc.json
whose contents are
{
"singleQuote": true,
"arrowParens": "avoid",
"printWidth": 120,
"semi": false,
"trailingComma": "none"
}
Unfortunately, my current team prefers "semi": true
—which is understandable
for people not familar with how JavaScript does semicolon insertion.
( If you’re having trouble getting prettier to work on save, consider using this package which detects your node modules’ path. )
Anyhow, with these rules:
- I write
const f = (x) => "x" =
x= then on save it becomesconst f = x => 'x' =
x;= - I write
let xs = [1, 2, 3,]
then on save it becomeslet xs = [1, 2, 3];
- I write
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());
then nothing happens on save; but if you add one more argument,, 12
, then it gets reformatted with each arg on one line.
Notice that in the first example above, only minor syntactic changes were made;
nothing altering semantics or quality of code —for that, we use a linter.
In particular, LSP ESLint when activated will show us two errors: It’s almost always
better to use ===
instead of ==
and literals should be on the right side of the equality.
We can use M-x lsp-execute-code-action
to get a list of actions that can be performed to fix this quality problem.
(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)
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:
- Coding in color: How to make syntax highlighting more useful —an excellent, terse, read
- C++ IDE Evolution: From Syntax Highlighting to Semantic Highlighting
- Names with a similar prefix share a colour, and class-local items share a colour.
- Lexical differential highlighting instead of syntax highlighting
- Ideally, the smaller the lexical difference, the greater the color difference should be.
- Colouring by Context —an Emacs package
- A case against syntax highlighting
(use-package highlight-quoted
:config (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode))
;; If everything worked fine, then “ 'b ” below should be coloured nicely in Emacs Lisp mode.
(when nil
(-let [x 'somevar]
(list x 'b "c" :e)))
(use-package highlight-numbers
:hook (prog-mode separedit-double-quote-string-mode)) ;; The latter is for when I do ⌘-e on a quoted string to edit it.
(use-package highlight-escape-sequences
:hook ((prog-mode . hes-mode)
(separedit-double-quote-string-mode . hes-mode)) ;; Wont work since this mode has no font-lock-builtin-face
:config
;; Colour the escapes as if they were builtin keywords.
(put 'hes-escape-backslash-face 'face-alias 'font-lock-builtin-face)
(put 'hes-escape-sequence-face 'face-alias 'font-lock-builtin-face))
;; TODO: My Emacs seems to have trouble loading the following and so I'm doint it manually.
(load-file "~/.emacs.d/elpa/company-posframe-20230104.1229/company-posframe.el")
(load-file "~/.emacs.d/elpa/highlight-escape-sequences-20201214.1730/highlight-escape-sequences.el")
(load-file "~/.emacs.d/elpa/parent-mode-20240210.1906/parent-mode.el")
(load-file "~/.emacs.d/elpa/highlight-numbers-20181013.1744/highlight-numbers.el")
;; If the above two worked fine, then you should see \n and 3 highlighted below
(when nil "Look: 1 and \\ and \n 2" (setq three 3))
Usually Emacs only highlights macro names, the following incantation makes it
highlight all defined names —as long as we’re in Lisp mode, whence in org-src
blocks we use C-c '
.
;; Emacs Lisp specific
(use-package highlight-defined :hook emacs-lisp-mode)
(load-file "~/.emacs.d/elpa/highlight-defined-20210411.222/highlight-defined.el")
Super helpful in making my Emacs configuration: If a name is not highlighted, then I’ve misspelled it or it doesn’t exist! 😄
(defun show-me ()
"Evaluate a Lisp expression and insert its value
as a comment at the end of the line.
Useful for documenting values or checking values.
"
(interactive)
(-let [it
(thread-last (thing-at-point 'line)
read-from-string
car
eval
(format " ;; ⇒ %s"))]
(end-of-line)
(insert it)))
(use-package smartparens
:init
(smartparens-global-mode 1)
(show-smartparens-global-mode +1)
:bind (
("M-f" . sp-forward-sexp) ;; Move forward one expression.
("M-b" . sp-backward-sexp) ;; Move backward one expression.
;; Going to the start & end of current expr in pair-able character.
("M-a" . sp-beginning-of-sexp)
("M-e" . sp-end-of-sexp)
;; Going forwards deep down & up current expr; treating it as a tree.
("M-d" . sp-down-sexp)
("M-u" . sp-up-sexp)
;; Acending & descending backwards; i.e., leftwards.
("M-n" . sp-backward-down-sexp)
("M-p" . sp-backward-up-sexp)
;; Unwrapping: Removing pair-able characters.
("M-w" . sp-unwrap-sexp)
("M-m" . sp-backward-unwrap-sexp)
;; “Slurping”: Move closing character forward/backward to include next sexp.
;; “Barfing”: Contract a sexp, or string, by pushing a its last/first item out.
;; See below for examples.
("M-)" . sp-forward-slurp-sexp)
("M-(" . sp-backward-slurp-sexp)
("M-]" . sp-forward-barf-sexp)
("M-[" . sp-backward-barf-sexp)
;; Transpose two bracketed terms; e.g., a b c ⟪Here⟫ ⟶ a c b ⟪Here⟫
;; Transpose backwards by being on the token;
;; transpose forwards by being after the token.
("M-t" . sp-transpose-sexp)
)
:config
;; Enable smartparens everywhere
(use-package smartparens-config)
(setq
;; smartparens-strict-mode t
;; sp-autoinsert-if-followed-by-word t
;; sp-autoskip-closing-pair 'always
sp-hybrid-kill-entire-symbol nil)
;; In Elisp & org modes, do not ‘close’ a back-tick or single quote!
(sp-local-pair 'emacs-lisp-mode "`" nil :when '(sp-in-string-p))
(sp-local-pair 'emacs-lisp-mode "'" nil :when '(sp-in-string-p))
(sp-local-pair 'org-mode "`" nil :when '(sp-in-string-p))
(sp-local-pair 'org-mode "'" nil :when '(sp-in-string-p))
)
Wrapping
To enclose a token with a pair-able character, at the start of the expression
press C-ESCAPE-SPACE
, select the region, followed by a pair-able character such as [, {, ", ', *,
etc.
To wrap a single token forwards, use C-M-SPACE
.
Examples of slurping & barfing –i.e., sexp inclusion and contraction.
a [x y z] b ⟶“M-) inside [⋯]”⟶ a [x y z b] a [x y z] b ⟶“M-) inside [⋯]”⟶ [a x y z] b [a x y z b] ⟶“M-] inside [⋯]”⟶ a [x y z b] [a x y z b] ⟶“M-] inside [⋯]”⟶ [a x y z] b
Have a thin line to the right to ensure I don’t write “off the page”.
(use-package fill-column-indicator
:hook (prog-mode . fci-mode))
;; (setq fill-column 120)
(use-package company-posframe :hook prog-mode)
;; I want to see the doc pop-ups nearly instantaneously 😅
(setq company-posframe-quickhelp-delay 0)
Anywhere in a macro form, press C-c e
to start an (inline) interactive session
where you can see what the expanded form of the macro looks like and you can
move your cursor to other macro forms and expand those as well by pressing
e
. Then press q
to quit.
(use-package macrostep)
(define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)
This seems like an excellent way to debug macros.
(use-package elisp-def)
(bind-key* "M-." #'elisp-def emacs-lisp-mode-map)
;; Example usage:
(when nil
(let ((foo 1))
(setq foo 2))) ;; “M-.” on this “foo” will now take us to the start of the let-clause.
Emacs can be setup with a spellchecker and other expected features of a word processing tool —however these features apply Emacs-wide since nearly everything is essentially text (•̀ᴗ•́)و
- Org-mode is a writer’s best friend; it’s large enough to deserve its own sections.
- See Emacs Writing Studio for a series of articles on making/using Emacs for writing.
;; Sometimes I have Arabic in my buffers, however I am an English speaker
;; and so Left-to-Right is most natural to me. As such, even when Arabic
;; is present, or any bidirectional text, just use Left-to-Right.
(setq-default bidi-paragraph-direction 'left-to-right)
(add-hook 'before-save-hook 'whitespace-cleanup)
See here for making whitespace visible; including spaces, tabs, and newlines
The following incantation, doc:my/org-mode-format, makes it so that we can select some text then press kbd:C-c_f (to get a list of possible character completions) then press the symbol we want our text to be surrounded with.
In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting paragraphs. This is a powerful form of “word wrap”.
(setq-default fill-column 80 ;; Let's avoid going over 80 columns
truncate-lines nil ;; I never want to scroll horizontally
indent-tabs-mode nil) ;; Use spaces instead of tabs
Certain variables are sensibly local to a buffer, and so setq
only alters their value for one buffer. Using setq-default
we change a variable’s default value, in every buffer.
;; Wrap long lines when editing text
(add-hook 'text-mode-hook 'turn-on-auto-fill)
(add-hook 'org-mode-hook 'turn-on-auto-fill)
We may press M-q
to cleverly redistribute the line breaks within any paragraph, thereby making it look better. With a
prefix argument, it justifies it as well —i.e., pads extra white space to make the paragraph appear rectangular.
Note that M-o M-s
centres a line of text ;-) Fun stuff!
Fill-mode is also known as “hard word wrapping”, which has the counterpart “soft word wrapping” …
Visual line mode is built-in and provides support for editing by visual lines: Lines off the screen are visually word
wrapped, but logically remain one line. Moreover C-a,e,k
operate on visual lines rather than logical lines.
;; Bent arrows at the end and start of long lines.
(setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow))
(global-visual-line-mode 1)
Visual line mode is useful when I have way too many windows open or when using smaller frames.
When writing, it’s common to use +,-,*
to enumerate unordered lists
—especially so in Org-mode wherein they denote structured text. Let’s render
them visually as Unicode bullets.
;; (x y z) ≈ (existing-item replacement-item positivity-of-preceding-spaces)
(cl-loop for (x y z) in '(("+" "◦" *)
("-" "•" *)
("*" "⋆" +))
do (font-lock-add-keywords 'org-mode
`((,(format "^ %s\\([%s]\\) " z x)
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ,y)))))))
I would like to check spelling on the fly.
C-;
- Cycle through corrections for word at point.
M-$
- Check and correct spelling of the word at point
M-x ispell-change-dictionary RET TAB
- To see what dictionaries are available.
Install spell-checking application as well as a reliable English dictionary, WordNet.
(system-packages-ensure "aspell")
(system-packages-ensure "wordnet")
flyspell-prog-mode
enables spell checking for programming by only considering
comments and strings.
(use-package flyspell
:hook ((prog-mode . flyspell-prog-mode)
((org-mode text-mode) . flyspell-mode)))
Enabling fly-spell for text-mode enables it for org and latex modes since they derive from text-mode.
Flyspell needs a spell checking tool, which is not included in Emacs. We
install aspell
spell checker using, say, homebrew via brew install aspell
. Note
that Emacs’ ispell
is the interface to such a command line spelling utility.
(setq ispell-program-name (s-trim (shell-command-to-string "which aspell")))
(setq ispell-dictionary "en_GB") ;; set the default dictionary
[Disabled] Allow spelling support for CamlCase words like “EmacsIsCool”.
(setq ispell-extra-args '("--sug-mode=ultra"
"--run-together"
"--run-together-limit=5"
"--run-together-min=2"))
Let us select a correct spelling merely by clicking on a word —for the rare days I have a mouse.
(eval-after-load "flyspell"
' (progn
(define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word)
(define-key flyspell-mouse-map [mouse-3] #'undefined)))
Colour incorrect works; default is an underline.
(global-font-lock-mode t)
(custom-set-faces '(flyspell-incorrect ((t (:inverse-video t)))))
Finally, save to user dictionary without asking:
(setq ispell-silently-savep t)
Let’s keep track of my personal word set by having it be in my version controlled
.emacs directory. Note that the default location is ~~/.[i|a]spell.DICT~ for
a specified dictionary DICT
.
(setq ispell-personal-dictionary "~/.emacs.d/.aspell.en.pws")
Nowadays, I very rarely write non-literate programs, but if I do I’d like to check spelling only in comments/strings. E.g.,
(add-hook 'c-mode-hook 'flyspell-prog-mode)
(add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode)
Use the thesaurus Emacs frontend Synosaurus to avoid unwarranted repetition.
(use-package synosaurus
:defer 100
:init (synosaurus-mode)
:config (setq synosaurus-choose-method 'popup) ;; 'ido is default.
(global-set-key (kbd "M-#") 'synosaurus-choose-and-replace))
The thesaurus is powered by the Wordnet wn
tool, which can be invoked without an
internet connection!
;; (shell-command "brew cask install xquartz &") ;; Dependency
;; (shell-command "brew install wordnet &")
Let’s use Wordnet as a dictionary via the wordnut package.
(use-package wordnut
:defer 100
:bind ("M-!" . wordnut-lookup-current-word))
;; Use M-& for async shell commands.
Use M-↑,↓
to navigate dictionary results, and wordnut-search
for a new search.
An alternative to wordnut
is to use the lightweight define-word
package; which I
think is not ideal since it provides way less information.
(load "~/dotfiles/.emacs.d/powerthesaurus.el")
(global-set-key (kbd "M-#") 'powerthesaurus-lookup-word-at-point)
;; Website currently down ... https://github.com/SavchenkoValeriy/emacs-powerthesaurus/issues/6
Use this game to help you learn to spell words that you’re having trouble with;
e.g., I have a file ~~/Dropbox/spelling.txt~ with words I have trouble spelling,
which I open then run M-x typing-of-emacs
in order to improve spelling said
words.
;; The Typing Of Emacs, a game.
(use-package typing-of-emacs
:quelpa (typing :fetcher wiki :url "https://www.emacswiki.org/emacs/typing.el"))
Practice touch typing using speed-type.
(use-package speed-type )
Running M-x speed-type-region
on a region of text, or M-x speed-type-buffer
on a
whole buffer, or just M-x speed-type-text
will produce the selected region, buffer,
or random text for practice. The timer begins when the first key is pressed
and stats are shown when the last letter is entered.
Other typing resources include:
- Typing of Emacs —an Emacs alternative to speed type, possibly more engaging.
- Klavaro —a GUI based yet language-independent typing tutor.
- I’m enjoying this tool in getting started with Arabic typing.
- Typing.io is a tutor for coders: Lessons are based on open source code, such some XMonad written in Haskell or Linux written in C.
- GNU Typist —which is interactive in the terminal, so not ideal in Emacs–,
To assist in language learning, it may be nice to have an Emacs
interface to Google translate —e.g., invoke google-translate-at-point
.
(use-package google-translate
:config
(global-set-key "\C-ct" 'google-translate-at-point))
Select the following then C-c t
,
Hey buddy, what’re you up to?
Then detect language then Arabic to obtain:
مرحباً يا صديقي ، ماذا تفعل؟
Neato 😲
[ A possibly better alternative is Vale. ]
Let’s install a grammar and style checker. We get the offline tool from the bottom of the LanguageTool website, then relocate it as follows.
(use-package langtool
:custom
(langtool-language-tool-jar
"~/Applications/LanguageTool-4.5/languagetool-commandline.jar"))
Now we can run langtool-check
on the subsequent grammatically incorrect
text —which is from the LanguageTool website— which colours errors in red,
when we click on them we get the reason why; then we may invoke
langtool-correct-buffer
to quickly use the suggestions to fix each correction,
and finally invoke langtool-check-done
to stop any remaining red colouring.
LanguageTool offers spell and grammar checking. Just paste your text here and click the 'Check Text' button. Click the colored phrases for details on potential errors. or use this text too see an few of of the problems that LanguageTool can detecd. What do you thinks of grammar checkers? Please not that they are not perfect. Style issues get a blue marker: It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017 --uh oh, that's the wrong date ;-)
By looking around the source code, I can do all three stages smoothly (•̀ᴗ•́)و
;; Quickly check, correct, then clean up /region/ with M-^
(eval-after-load 'langtool
(progn
(add-hook 'langtool-error-exists-hook
(lambda ()
(langtool-correct-buffer)
(langtool-check-done)))
(global-set-key "\M-^"
(lambda ()
(interactive)
(message "Grammar checking begun ...")
(langtool-check)))))
The checking command is silent, we added a bit of comforting acknowledgement to the user.
Let’s write good!
(use-package writegood-mode
;; Load this whenver I'm composing prose.
:hook (text-mode org-mode)
;; Don't show me the “Wg” marker in the mode line
:defer 100
;; Some additional weasel words.
:config
(--map (push it writegood-weasel-words)
'("some" "simple" "simply" "easy" "often" "easily" "probably"
"clearly" ;; Is the premise undeniably true?
"experience shows" ;; Whose? What kind? How does it do so?
"may have" ;; It may also have not!
"it turns out that"))) ;; How does it turn out so?
;; ↯ What is the evidence of highighted phrase? ↯
Inspired by Matt Might’s 3 shell scripts to improve your writing, or “My Ph.D. advisor rewrote himself in bash”, this Emacs interface emphasises, via underline, the following weaknesses in writing —so that I can fix them or decide that they are appropriate for the scenario.
Sentences that cut out the following problems may become stronger —by being more terse or precise.
- Weasel Words
-
Phrases that sound good without conveying information;
such as vague precision or subjective phrases.
E.g., a number of, surprisingly, very close.
It’s okay not to have exact details, but rather than “I don’t know” explain why not and what the next steps will be.
- Passive Voice
-
Phrases wherein interest is in the object experiencing an action,
rather than the subject that performs the action.
- Bad: The house was built by my father.
- Good: My father built this house.
Likewise, including relevant or explanatory information as in “X guarantees Y” is an improvement over “Y is guaranteed”.
Sometimes the subject really is irrelevant, such as “We did X” whereas “X happened” suffices.
👍 If the relevant subject is unclear and, also, the text reads better in the active, then change a phrase.
- Duplicated Words
- Occurrences of, say, “the the”.
Harder to catch manually, but easier mechanically ;-)
When learning about Emacs formatting commands, such as zap-to-char M-z
or transpose M-t
, it’s best to have filler text —even better when
it’s automatically generated instead of typing it out ourselves. The
following will give us a series of commands lorem-ipsum-insert-⋯
for
inserting lists, sentences, paragraphs and using a prefix argument,
with C-u
, we can request to generate any number of them.
(use-package lorem-ipsum )
‘Lorem’ is not a word itself, but it comes from the Latin ‘Dolorem Ipsum’ which means “pain in and of itself”.
See this Emacs Cheat Sheet to try out the textual navigation and formatting bindings on lorem ipsum, gibberish text.
The dad-joke queries https://icanhazdadjoke.com to bring us some funny.
(use-package dad-joke
:config (defun dad-joke () (interactive) (insert (dad-joke-get))))
For example, M-x dad-joke
now inserts:
What are the strongest days of the week? Saturday and Sunday…the rest are weekdays.
;; (load (shell-command-to-string "agda-mode locate"))
;;
;; Seeing: One way to avoid seeing this warning is to make sure that agda2-include-dirs is not bound.
; (makunbound 'agda2-include-dirs)
Agda is one of my favourite languages, it’s like Haskell on steroids. Let’s set it up for the main sake of its Unicode input —you may do likewise using TeX input. ( The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) )
Agda input mode makes it extremely easy to use unicode in documents, something I strongly prefer to do. When I can use symbols directly, instead of (for instance) LaTeX commands, it makes my plaintext far more readable. — Armkeh .emacs config
(system-packages-ensure "agda")
Invoke brew info agda
to get these instructions and the version of Agda just
installed.
Executing agda-mode setup
appends the following text to the .emacs
file.
Let’s put it here ourselves.
(unless noninteractive
(load-file (let ((coding-system-for-read 'utf-8))
(shell-command-to-string "agda-mode locate"))))
I almost always want the agda-mode
input method —it’s like the TeX method, but
better.
;; TODO: Maybe don't bother installing Agda, and just get agda-input.el
;; from: https://github.com/agda/agda/blob/master/src/data/emacs-mode/agda-input.el
;; then loading that!
(url-copy-file "https://raw.githubusercontent.com/agda/agda/master/src/data/emacs-mode/agda-input.el" "~/.emacs.d/elpa/agda-input.el" :ok-if-already-exists)
(load-file "~/.emacs.d/elpa/agda-input.el")
;; MA: This results in "Package cl is deprecated" !?
(unless noninteractive
(use-package agda-input
:ensure nil ;; I have it locally.
:demand t
:hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda")))
:custom (default-input-method "Agda")))
;; Now C-\ or M-x toggle-input-method turn it on and offers
;; TODO add a hook that when the input method becomes Agda, just don't bother showing me in the modeline.
;; E.g., "Π" when using unicode input with Agda
;; Useful to have in the modeline, say when typing in Arabic.
;; (add-variable-watcher
;; 'current-input-method
;; (lambda (_ newvalue 'set _)
;; (setq current-input-method-title
;; (if (equal newvalue "Agda") nil newvalue))))
Wolfram Kahl has recommended the following settings.
;;(setq agda2-program-args (quote ("RTS" "-M4G" "-H4G" "-A128M" "-RTS")))
These arguments specify
+RTS , -RTS | Flags between these are arguments to the ghc runtime |
-M[size] | Maximum heap size |
-H[size] | Suggested heap size |
-A[size] | Allocation area size used by the garbage collector |
Full documentation for the ghc
runtime argumentscan be found here.
Additional arguments that may be useful include
-S[file] | Produces information about “each and every garbage collection” |
- Outputs to stderr by default |
Unicode doesn’t intend to cover things that are achievable with markup, so only a limited subset of the alphabet is available as subscript; but all is available as superscript, except ‘q’.
ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ ᴬ ᴮ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴿ ᵀ ᵁ ⱽ ᵂ ᵅ ᵝ ᵞ ᵟ ᵋ ᶿ ᶥ ᶲ ᵠ ᵡ ᵦ ᵧ ᵨ ᵩ ᵪ
brew cask install font-symbola
⇒ Includes fonts for subscripts; e.g., ₐₙₑₕᵢⱼₖₗₘₙₒₚₜₛ
Below are my personal Agda input symbol translations;
e.g., \set → 𝒮ℯ𝓉
. Note that we could give a symbol new Agda TeX binding
interactively: M-x customize-variable agda-input-user-translations
then
INS
then for key sequence type set
then INS
and for string paste 𝒮ℯ𝓉
.
(unless noninteractive (add-to-list 'agda-input-user-translations '("set" "𝒮ℯ𝓉")))
Better yet, as a loop:
(unless noninteractive
(cl-loop for item
in '(;; Arabic ornate parenthesis U+FD3E / U+FD3F
("(" "﴾")
(")" "﴿")
("cmd" "⌘")
;; categorial ;;
("alg" "𝒜𝓁ℊ")
("split" "▵")
("join" "▿")
("adj" "⊣")
(";;" "﹔")
(";;" "⨾")
(";;" "∘")
;; logic
("if" "⇐")
("onlyif" "⇒")
;; lattices ;;
("meet" "⊓")
("join" "⊔")
;; tortoise brackets, infix relations
("((" "〔")
("))" "〕")
;; residuals
("syq" "╳")
("over" "╱")
("under" "╲")
;; Z-quantification range notation ;;
;; e.g., “∀ x ❙ R • P” ;;
("|" "❙")
("with" "❙")
;; Z relational operators
("domainrestriction" "◁")
("domr" "◁")
("domainantirestriction" "⩤")
("doma" "⩤")
("rangerestriction" "▷")
("ranr" "▷")
("rangeantirestriction" "⩥")
("rana" "⩥")
;; adjunction isomorphism pair ;;
("floor" "⌊⌋")
("lower" "⌊⌋")
("lad" "⌊⌋")
("ceil" "⌈⌉")
("raise" "⌈⌉")
("rad" "⌈⌉")
;; Replies
("yes" "✔")
("no" "❌")
;; Arrows
("<=" "⇐")
;; more (key value) pairs here
)
do (add-to-list 'agda-input-user-translations item)))
Also some silly stuff:
(unless noninteractive
;; Add to the list of translations using “emot” and the given, more specfic, name.
;; Whence, \emot shows all possible emotions.
(cl-loop for emot
in `(;; angry, cry, why-you-no
("whyme" "ლ(ಠ益ಠ)ლ" "ヽ༼ಢ_ಢ༽ノ☂" "щ(゜ロ゜щ)" "‿︵(ಥ﹏ಥ)‿︵" "ಠ_ಠ" "(╬ ಠ益ಠ)" "・゚(*❦ω❦)*・゚" "(╯°□°)╯︵ ┻━┻") ;; flip the table
;; confused, disapprove, dead, shrug, awkward
("what" "「(°ヘ°)" "(ಠ_ಠ)" "(✖╭╮✖)" "¯\\_(ツ)_/¯" "(´°ω°`)" "・✧_✧・")
;; dance, csi
("cool" "┏(-_-)┓┏(-_-)┛┗(-_- )┓"
,(s-collapse-whitespace "•_•)
( •_•)>⌐■-■
(⌐■_■)"))
;; love, pleased, success, yesss, smile, excited, yay
("smile" "♥‿♥" "(─‿‿─)" "(•̀ᴗ•́)و" "ᕦ( ᴼ ڡ ᴼ )ᕤ" "(งಠ_ಠ)ง" "(。◕‿◕。)" "(◕‿◕)" "( ˃ ヮ˂)" "[ ⇀ ‿ ↼ ]" "٩(⁎❛ᴗ❛⁎)۶" "ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ" "(✿◠‿◠)")
;; flower high-5
("hug" "♡(✿ˇ◡ˇ)人(ˇ◡ˇ✿)♡" "(づ。◕‿◕。)づ" "(づ。◕‿‿‿‿◕。)づ"))
do
(add-to-list 'agda-input-user-translations emot)
(add-to-list 'agda-input-user-translations (cons "emot" (cdr emot)))))
Finally let’s effect such translations.
;; activate translations
(unless noninteractive (agda-input-setup))
Note that the effect of Emacs unicode input could be approximated using
abbrev-mode
.
C-±
…
(global-set-key (kbd "C-+") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)
;; C-x C-0 restores the default font size
If thou knowst the ELisp, forgive this shadowing of the negative-argument
… we’ve still got M--
though.
Curious, this is one of the very first things I did when began using Emacs; yet, perhaps I would not have done it if I was simply told the defaults:
C-x C-=,+
increases text sizeC-x C--
decreases test sizeC-x C-0
restores it to the default size
So, the above snippet seems to save us of the prefix
C-x
and we lose on using ‘=’ for text increase and worse we
need the shift-key to get access to the ‘+’.
I suppose this is just a habit inherited from using other tools. Fortunately, I
did not inherit the need for the common user access bindings C-x
kill, C-c
copy,
C-v
paste, nor C-z
undo of other applications. If you’re interested, M-x
cua-mode
to enable CUA Bindings.
This extends Org-mode’s M-↑,↓
to other modes, such as when coding.
;; M-↑,↓ moves line, or marked region; prefix is how many lines.
(use-package move-text
:config (move-text-default-bindings))
Subword movement lets us treat “EmacsIsAwesome” as three words
─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming
is common among coders. Now, for example, M-f
moves along each subword.
(global-subword-mode 1)
Text selected with the mouse is automatically copied to clipboard.
(setq mouse-drag-copy-region t)
Delete Selection mode lets you treat an Emacs region much like a typical text selection outside of Emacs: You can replace the active region. We can delete selected text just by hitting the backspace key.
(delete-selection-mode 1)
Let’s mimic the C-n,p
constructs from line to word, so that unoccupied M-n,p
now
serve to take us to the next or previous instance of the word under the
cursor. This is less intrusive than searching C-s
or listing all occurrences M-s
o
. Moreover, let’s automaticly highlight current symbols, words under the cursour, and cycle
quickly between highlkighted locations with
ahs-forward
/ kbd:M-→ and
ahs-backward
/ kbd:M-←.
;; Default: M-→/← moves to the next/previous instance of the currently highlighted word
;; These are already meaningful commands in Org-mode, so we avoid these key re-bindings in Org-mode; TODO.
(use-package auto-highlight-symbol)
;; :hook ((text-mode . auto-highlight-symbol-mode)
;; (prog-mode . auto-highlight-symbol-mode)))
;;
;; This breaks Org Exports; e.g.,
;; C-c C-e h o ⇒ Match data clobbered by buffer modification hooks
Let’s also quickly replace the word at point:
(defun my/symbol-replace (replacement)
"Replace all standalone symbols in the buffer matching the one at point."
(interactive (list (read-from-minibuffer "Replacement for thing at point: " nil)))
(save-excursion
(let ((symbol (or (thing-at-point 'symbol) (error "No symbol at point!"))))
(beginning-of-buffer)
;; (query-replace-regexp symbol replacement)
(replace-regexp (format "\\b%s\\b" (regexp-quote symbol)) replacement))))
(Unfortunately, as it currently is, there is no universal argument support: C-u
2 M-p
does not take you to the second previous instance of a word —the prefix
is instead ignored. We can quickly form a work-around with doc:defhydra —which
gives us temoporary modal modes.)
Finally, let’s use kbd:M-n ╱ M-p ╱ M-’ to move to the next, previous, and replace the word at point but also to give us a meanu to repeat any of these actions.
(defmacro my/make-navigation-hydra (initial-action)
`(defhydra word-navigation
(:body-pre (,initial-action)) "Word-at-point Navigation"
("n" ahs-forward "Next instance")
("p" smartscan-symbol-go-backward "Previous instance")
("r" my/symbol-replace "Replace all occurances")
("s" ahs-display-stat "Stats")))
;; (bind-key* str func) ≈ (global-set-key (kbd str) func)
(bind-key* "M-n" (my/make-navigation-hydra ahs-forward))
(bind-key* "M-p" (my/make-navigation-hydra ahs-backward))
(bind-key* "M-'" (my/make-navigation-hydra my/symbol-replace))
An alternative is the smart-scan library —which does the same thing, but does not provide the highlighting occurances at point.
At a glance of possible positions, across windows, and a key to jump there is a feature provided to us by ace-jump —here is an emacs-rocks 2-minute video.For example, C-c SPC m
greys our all windows and places a red
letter at the start of any word that begins with m, then I may
press a letter to jump to the associated position in the
associated window. Using C-u C-c SPC
and C-u C-u C-c SPC
let
me jump to any character or to any visible line.
➩ Super simple use case: Fix your eyes on an occurence of a word, then C-c SPC
to quickly jump to it so as to edit the sentence in which it occurs.
- It’s like
C-s
but more lightweight.
(use-package ace-jump-mode
:config (bind-key* "C-c SPC" 'ace-jump-mode))
;; See ace-jump issues to configure for use of home row keys.
There is a newer and somewhat more powerful package, avy, which accompishes the same goal. It uses a tree style to jumipng: Locations are given two letter combinations, one presses one letter to jump to a group of text, then another letter to jump somewhere in that grouping. I prefer ace-jump since it greys everthing out, whereas avy surrounds jump locations with a box. Here is an emacs-doom 6-minute video for avy.
There is also ace-isearch for bridinging different navgiational methods —one
begins incremental search, s-f
, then according to a pause and length of input,
one of the navgiational methods, such as isearch or avy or helm-swoop, will be
begun. I’m okay with using C-s
for helm-swoop and C-c SPC
for ace-jump, and
still have s-f
for incremental search, which I hardly use.
What is bind-keys*?
Major modes provide specfic use and so their bindings always take precedence
over global bindings —e.g., the major mode binding may do what the global does
but with extra mode-specfic behaviour, such as indentation. Other times, a major
mode’s binding simply uses the same key presses with completely unrelated
behaviour. If we want to avoid having our global keybindings shadowed by a
major mode, we may use the bind-key*
macro of use-package
, or the bind-keys*
macro when there are multiple keys; these are macros, not clauses. —These
essentially creates a dedicated minor mode behind the scenes, which saves us the
work of doing it ourselves.
(bind-keys* (k₁ . f₁) … (kₙ . fₙ)) | |
≈ | These keybindings override all minor modes that use keys kᵢ . |
Of course we can also use it without the asterisk; e.g.:
;; C-x o ⇒ Switch to the other window
;; C-x O ⇒ Switch back to the previous window
(bind-key "C-x O" (lambda () (interactive) (other-window -1)))
;; Give me a description of the change made at a particular stop.
(use-package goto-chg
:custom (glc-default-span 0))
(my/defhydra "C-c e" "Look at them edits!" bus
:\ ("p" goto-last-change "Goto nᵗʰ last change")
("n" goto-last-change-reverse "Goto more recent change"))
Compare this with C-x u
, or undo-tree-visualise
, wherein undos are actually performed.
Notice, as a hydra, I can use C-c e
followed by any combination of p
and n
to
navigate my recent edits without having to supply the prefix each time.
;; While constructing the regexp in the minibuffer, get live visual feedback for the (group) matches.
;; E.g., try: M-% use-\(.+?\) \(.+\)\b ENTER woah \1 and \2
;;
;; C-u M-% do to regexp replace, without querying.
(use-package visual-regexp
:config (define-key global-map (kbd "M-%")
(lambda (&optional prefix) (interactive "P") (call-interactively (if prefix #'vr/replace #'vr/query-replace)))))
In this section we consider the Org-mode export for PDFs (LaTeX). For example, we account for LaTeX citations.
#
(system-packages-ensure "mactex")
- This is a redistribution of TeX Live specifically for macOS.
- We get the 4GB version since it has everything and so do not need to worry about missing style files.
- This took about 12 minutes on my machine.
Restart Emacs, enter $e^{i \cdot \pi} + 1 = 0$
then press kbd:C-c_C-x_C-l to
have it rendered inline.
- Minted: Get tool for colourful code snippets for LaTeX —see “minted” in
the main article.
(system-packages-ensure "pygments")
- Not anymore: Get a neato PDF presentation console:
brew install pdfpc
- With
pdfpc myfile.pdf
you get a nice timer and multiple views of the current slide and upcoming slides —with support for multiple monitors. - Install ScreenBrush, from the Apple Store, for easily drawing/annotating my screen —e.g., when I’m giving a virtual lecture to my students.
- An alternative is
brew install mupdf
thenmupdf-gl myfile.pdf
and pressf
for fullscreen thena
for adding/adorning drawings —it was too rough to use live.
Finally, within Emacs:
M-x pdf-tools-install
- With
;; Use 3 headlines of export, which is the default
;; (setq org-export-headline-levels 4)
;; no numbers by default at export
;; (setq org-export-with-section-numbers nil)
An exquisite system for handling references.
The following entity will display useful data when the mouse hovers over it (•̀ᴗ•́)و If you click on it, then you’re in for a lot of super neat stuff, such as searching for the pdf online!
cite:agda_overview ( In HTML export, the citation doesn’t link anywhere. )
(unless noninteractive
(use-package org-ref
:custom ;; Files to look at when no “╲bibliography{⋯}” is not present in a file.
;; Most useful for non-LaTeX files.
(reftex-default-bibliography '("~/thesis-proposal/papers/References.bib"))
(bibtex-completion-bibliography (car reftex-default-bibliography))
(org-ref-default-bibliography reftex-default-bibliography))
;; Quick BibTeX references, sometimes.
(use-package helm-bibtex)
(use-package biblio) )
Execute M-x helm-bibtex
or C-c ]
and, say, enter emacs
and you will be
presented with all the entries in the bib database that mention ‘emacs’. Super
cool stuff. Moreover, if no such entries exist, then we can look some up
using the interface!
Read the manual online or better yet as an org-file with M-x org-ref-help
.
This is an Org-mode application since the citations have tooltips and export nicely to LaTeX & HTML via the Org-mode exporter.
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.
In this section we consider the Org-mode exporters for PDFs and HTMLs. For example, we account for LaTeX citations and reliable HTML anchors.
(use-package htmlize )
;; Main use: Org produced htmls are coloured.
;; Can be used to export a file into a coloured html.
(defun my/org-drawer-format (name contents)
"Export to HTML the drawers named with prefix ‘fold_’, ignoring case.
The resulting drawer is a ‘code-details’ and so appears folded;
the user clicks it to see the information therein.
Henceforth, these are called ‘fold drawers’.
Drawers without such a prefix may be nonetheless exported if their
body contains ‘:export: t’ ---this switch does not appear in the output.
Thus, we are biased to generally not exporting non-fold drawers.
One may suspend export of fold drawers by having ‘:export: nil’
in their body definition.
Fold drawers naturally come with a title.
Either it is specfied in the drawer body by ‘:title: ⋯’,
or otherwise the drawer's name is used with all underscores replaced
by spaces.
"
(let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents))
(fold? (s-prefix? "fold_" name 'ignore-case))
(export? (string-match ":export:\s+t" contents))
(not-export? (string-match ":export:\s+nil" contents))
(title′ (and (string-match ":title:\\(.*\\)\n" contents)
(match-string 1 contents))))
;; Ensure we have a title.
(unless title′ (setq title′ (s-join " " (cdr (s-split "_" name)))))
;; Output
(cond
((and export? (not fold?)) contents′)
(not-export? nil)
(fold?
(thread-last contents′
(replace-regexp-in-string ":title:.*\n" "")
(format "<details class=\"code-details\"> <summary> <strong>
<font face=\"Courier\" size=\"3\" color=\"green\"> %s
</font> </strong> </summary> %s </details>" title′))))))
(setq org-html-format-drawer-function 'my/org-drawer-format)
With the following invocations we only see the odd indexed ‘hello’s, where the latter two are folded up.
:this-drawer-is-exported:
:export: t
hello 1
:End:
:this-drawer-is-NOT-exported:
hello 2
:End:
:fold_This_drawer_has_a_title_in_the_body:
:title: I am the drawer title 0
hello 3
:End:
:fold_This_drawer_is_NOT_exported:
:title: Why are we here?
:export: nil
hello 4
:End:
:fold_I_am_the_drawer_title_1:
hello 5
:End:
I doubt I could show an example in the Github README, since no HTML export is happening using my setup. In case you’re reading this on my blog, which has exported HTML. Here’s the example:
Hey bud, hope you’re enjoying this read ^_^Now that I’ve written this, I’m thinking it may have been preferably to use an org-block…?
Let’s try out an alternative to PlantUML —covered below in §Workflow States.
First, let’s get the tool.
npm install mermaid.cli
sudo git clone [email protected]:arnm/mermaid-layer.git ~/.emacs.d/private/mermaid
Then, let’s get the associated mermaid
package.
(use-package ob-mermaid
:custom ob-mermaid-cli-path "~/node_modules/.bin/mmdc")
Then, C-c C-c
on the following:
sequenceDiagram
A-->B: Works!
C-c C-x C-v
⇒ Show images inline- Mermaid supported headers:
file
to name the svg/png/pdf outputwidth
orheight
or the resulting imagetheme
used, such asdefault, forest, dark, neutral
, for foreground entitiesbackground-color
such astransparent, red, #F0F0F0
- The transparent option is nice ^_^
- You can insert new lines using
<br>
and horizontal rules via<hr>
. Similarly you can use other HTML tags such as<center>
; if you have too many you can make CSS file then use the header argument:css-file
. - Add “non-breaking space” with
. This is a forced extra space and it prevents a line break at its location. You can insert it repeatedly, but for two spaces use 
and for four spaces use 
.
If link text cuts off prematurely, use extra space with a newline: A-- text
 <br> -->B
.
Warning: JavaScript has some issues when working with Unicode and so, being a
JavaScript utility, mermaid
hangs when Unicode is used. On the upside, being a
JavaScript utility, mermaid
entities can have arbitrary code attached to them to
be executed upon clicks —for use in browsers.
- However, the Greek letters are supported; e.g., γ and Σ.
See here for possible node shapes.
After forming an intricate diagram of related design patterns, I had to use a number of HTML notions, such as
<i>, <strong>, <em>, <h1>,  , <br>, <pre>, <center>
and it was a bit more than I would have liked. In particular, the only way to change font size was to use the deprecated HTML tag<big>
or heading tags like<h1>
; even worse, the resulting PDF image did not look nice —I had to stretch it out.The command line tool is lacking functionality and so the docs are not helpful. E.g., I cannot produce pie charts using the command line tool.
9.23.3 Reveal.JS – The HTML Presentation Framework
Org-mode documents can be transformed into beautiful slide decks with org-reveal with the following two simple lines.
(use-package ox-reveal
:custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"))
For example, execute, C-x C-e
after the closing parenthesis of, the
following block to see an example slide-deck (─‿‿─)
(progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org")
(switch-to-buffer (find-file "Trying_out_reveal.org"))
(org-reveal-export-to-html-and-browse))
Org-mode exporting, C-c C-e
, now includes an option R
for such reveal slide decks.
Two dimensional slides may be a bit new to some people, so I like to
give viewers an option, in tiny font, to view the slide-deck
continuously and remind them that ?
provides useful shortcuts.
(setq org-reveal-title-slide "<h1>%t</h1> <h3>%a</h3>
<font size=\"1\">
<a href=\"?print-pdf&showNotes=true\">
⟪ Flattened View ; Press <code>?</code> for Help ⟫
</a>
</font>")
One should remove the &showNotes=true
if they do not want to include
speaker notes in the flattened view.
Within the flatenned view, one may wish to CTRL/CMD+P
then save the
resulting PDF locally.
The following let’s us copy htlm into org format using eww, Emacs’ built-in web browser.
;; See: https://emacs.stackexchange.com/questions/7171/paste-html-into-org-mode
(use-package org-eww
:quelpa (org-eww :fetcher git :url "https://github.com/Fuco1/org-mode.git"))
It does not work as I’d like, but may prove useful to have around.
- Possibly useful: Open a webpage with
M-x eww
then toggleM-x read-only-mode
to edit the text, say for notes or deletions, as you read! No need to copy-paste.
org-web-tools claims to view, capture, and archive Web pages in Org-mode; this may be a very useful tool.
(use-package org-web-tools
:config
;; Insert an Org-mode link to the URL in the clipboard or kill-ring. Downloads
;; the page to get the HTML title.
;; (bind-key* "C-c C-l" #'org-web-tools-insert-link-for-url) ;; Instead, see my/org-insert-link-dwim below.
)
Other useful functions, needing pandoc:
org-web-tools-insert-web-page-as-entry
andorg-web-tools-convert-links-to-page-entries
.
;; C-u C-c C-l ⇒ Paste URL with title, WITHOUT prompting me for anything.
;; C-c C-l ⇒ Prompt me for title.
(bind-key* "C-c C-l"
(lambda () (interactive)
(call-interactively
(if current-prefix-arg
#'org-web-tools-insert-link-for-url
#'my/org-insert-link-dwim))))
;; From:
(defun my/org-insert-link-dwim ()
"Like `org-insert-link' but with personal dwim preferences.
- When text is selected, use that as the link description --and prompt for link type
- When a URL is in the clipboard, use that as the link type
- On an existing Org link, prompt to alter the link then to alter the description
- With a ‘C-u’ prefix, prompts for a file to link to.
- It is relative to the current directory; use ‘C-u C-u’ to get an absolute path.
It fallsback to `org-insert-link' when possible.
Functin Source: https://xenodium.com/emacs-dwim-do-what-i-mean/"
(interactive)
(let* ((point-in-link (org-in-regexp org-link-any-re 1))
(clipboard-url (when (string-match-p "^http" (current-kill 0))
(current-kill 0)))
(region-content (when (region-active-p)
(buffer-substring-no-properties (region-beginning)
(region-end)))))
(cond ((and region-content clipboard-url (not point-in-link))
(delete-region (region-beginning) (region-end))
(insert (org-make-link-string clipboard-url region-content)))
((and clipboard-url (not point-in-link))
(insert (org-make-link-string
clipboard-url
(read-string "title: "
(with-current-buffer (url-retrieve-synchronously clipboard-url)
(dom-text (car
(dom-by-tag (libxml-parse-html-region
(point-min)
(point-max))
'title))))))))
(t
(call-interactively 'org-insert-link)))))
A configuration file sets up various features for a tool —and serves as an essential learning point. In order to remember them, what they do, and possibly where you learned about them —which may include additional resources— it is pertinent to document such facts. Benefits of documentating features include:
- A list of the features with human readable names! —In case you forget what you invested time on!
- Personal documentation! —Reduce wasting time Googling things that you knew in the past!
- Convincing Need
- Making notes with decriptive text, as suggested below,
will make it clear whether you actually need the feature
or “just threw it becuase it looks cool” —which leads to ‘init bankruptcy’.
Moreover, actually documenting a feature may make it more to recall that you have the feature and have notes for it.
- Making notes with decriptive text, as suggested below,
will make it clear whether you actually need the feature
or “just threw it becuase it looks cool” —which leads to ‘init bankruptcy’.
Programs are meant to be read by humans and only incidentally for computers to execute. —Donald Knuth
Alongside a feature’s installation, I’ve tried to provide the following:
- Why would I want this? Motivation!
- Example scenerios and use-cases.
- How do I actually use it? Super terse usage details to “get going”!
- Where is the offical documentation page, or repository? Discovarability!
- Comparisions: Are there other similar features, builtin or otherwise? How do they compare? Why have I decided for this one instead of another one?
- Additional comments and reminders related to the feature.
- E.g., why the feature is now disabled, ‘commented out’, when before it was useful.
Programs without documentation have little value; it’s like a claim without evidence! —Me
Here are some benefits of having a tool’s configurations written literately as an Org-mode file, then tangeling as appropriate.
- Modularity! —or “In Praise of the Monolith”
It may not be feasible, or practical, to split a tool’s configuration file into multiple file hierarchy. Yet, with Org-mode we may reify the hierarchical structure as ‘sections’ and have the resulting configuration read more like a novel, easily folding and navigating, between sections.
- Section headers provide organisation and they’re collapsable.
Even if you can make multiple files, using one monolithic file allows:
- Really easy to quickly re-organise code!
- Use
w
to move content almost instanteously! - In contrast, it’s harder to review an entire project, when it’s in pieces.
- Use
- Many files requires coming up with descriptive file names; instead prefer descriptive org headings ^_^
- Easily navigatable hierarchy with a nested directory/org-heading structure.
- Have headings with an introducttory paragraph that explains the kind of features being considered —or, lazily, look at the outlined view of subheadings to see what’s there.
- Easy search & review of features since they’re in one file.
- Multiple files makes it harder to remember which features live where.
- One file is easy to distribute & share!
Many small files are great for collobaration —there’ll likely be less merge conflicts. However, configuration files are usually a one-person project.
- Toggle feature selection without altering any code!
With a single
#
key press, we can comment out a section, thereby disabling the features it provides. The features are neither deleted nor forgotten, but we can experiment with having them there or not without altering any code! Alternatively, one mays use the:noexport:
tag on a section header.In contrast, an illiterate setup would have us commenting out large chunks of code, which is not as easy to manage.
- Really easy to delete content!
After a while, I come back and realise I’ve implemented something silly or that is available via some external package, I can quickly delete it.
- Can quickly export to different mediums!
If you want to share your configuration with others, then an HTML rendition with a table of contents and text sprinkled everywhere is more likely to attract onlookers since they can easily jump to the sections they’re interested in.
- Easily digestible chunks of code!
With a literate approach, one is empowered to have short source blocks; e.g., not exceeding 30 lines —read more here. This is more likely to ensure (possibly by extracting code into its own functions): The listing fits on one screen, avoiding deeply nested control structures, non-repeating common logical patterns, increased confidence that the implementation meets the stated purpose.
The only reason I would use multiple files or raw code for setting up a tool would be if I did not have a literate programming environment; i.e., Org-mode.
Emacs is fun (•̀ᴗ•́)و
Bye!
Thanks to friends for “replacing me” while I was away ♥‿♥ ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ
(add-to-list 'default-frame-alist '(fullscreen . maximized))
(message-box "Enjoy life (。◕‿◕。))")