A Life Configuring Emacs
Table of Contents
- 1. Why Emacs?
- 1.1. Emacs is a flexible platform for developing end-user applications
- 1.2. The Power of Text Manipulation
- 1.3. Keyboard Navigation and Alteration
- 1.4. Emacs Proverbs as Koan
- 1.5. Possibly Interesting Reads
- 1.6. Fun commands to try out
- 1.7. What Does Literate Programming Look Like?
- 1.8. Why a monolithic configuration?
- 2. Booting Up
- 2.1.
~/.emacs
vs.init.org
- 2.2. Who am I?
- 2.3. Emacs Package Manager
- 2.4. Installing OS packages, and automatically keeping my system up to data, from within Emacs
- 2.5. Syncing to the System's
$PATH
- 2.6. Restarting Emacs —Keeping buffers open across sessions?
- 2.7. “Being at the Helm” —Completion & Narrowing Framework
- 2.8. Org-Mode Administrivia
- 2.9. Password-locking files —“encryption”
- 2.10. all-the-icons
- 2.11. Hydra: Supply a prefix only once
- 2.12. Helpful Utilities & Shortcuts
- 2.1.
- 3. Staying Sane
- 3.1. Undo-tree: Very Local Version Control
- 3.2. Automatic Backups
- 3.3.
magit
—Emacs' porcelain interface to git - 3.4. Pretty Magit Commit Leaders
- 3.5. Version Control with SVN —Using Magit! Disabled
- 3.6. Highlighting TODO-s & Showing them in Magit
- 3.7. Silently show me when a line was modified and by whom
- 3.8. delete-by-moving-to-trash t
- 3.9. Jumping to extreme semantic units
- 3.10. Get CheatSheets and view them easily
- 4. Literate Programming
- 5. Life within Org-mode
- 6. Cosmetics
- 6.1. Startup message: Emacs & Org versions
- 6.2. My to-do list: The initial buffer when Emacs opens up
- 6.3. A sleek, informative, & fancy mode line
- 6.4. Exquisite Fonts and Themes
- 6.5. Never lose the cursor
- 6.6. Dimming Unused Windows
- 6.7. Flashing when something goes wrong
- 6.8. Hiding Scrollbar, tool bar, and menu
- 6.9. Highlight & complete parenthesis pair when cursor is near ;-)
- 6.10. Proportional fonts for Headlines
- 6.11. Making Block Delimiters Less Intrusive
- 6.12. Hiding Emphasise Markers, Inlining Images, and LaTeX-as-PNG
- 6.13. Show off-screen heading at the top of the window
- 6.14. Powerful Directory Editing with
dired
- 6.15. Persistent Scratch Buffer
- 6.16. Tabs Disabled
- 6.17. Window resizing using the golden ratio Disabled
- 6.18. Org-Emphasise for Parts of Words Disabled
- 6.19. Preview link under cursor
- 6.20. Replace phrases with nice SVG labels
- 7. Prose
- 7.1. Whitespace
- 7.2. Formatting Text
- 7.3. Fill-mode —Word Wrapping
- 7.4. Pretty Lists Markers
- 7.5. Fix spelling as you type —thesaurus & dictionary too!
- 7.6. Using a Grammar & Style Checker
- 7.7. Lightweight Prose Proofchecking
- 7.8. Placeholder Text —For Learning & Experimenting
- 7.9. Some text to make us smile
- 7.10. Unicode Input via Agda Input
- 7.11. Increase/decrease text size
- 7.12. Moving Text Around
- 7.13. Enabling CamelCase Aware Editing Operations
- 7.14. Delete Selection Mode
- 7.15.
M-n,p
: Word-at-Point Navigation ╱╲ Automatic highlighting current symbol/word - 7.16. Letter-based Navigation
- 7.17.
C-c e n,p
: Taking a tour of one's edits - 7.18. visual-regexp
- 7.19. LaTeX ⇐ Org-Mode
- 7.20. HTML ⇐ Org-mode
- 8. Programming
- 8.1. Quickly Run Code Snippets
- 8.2. C-x C-e ∷ REPL-driven development for ELisp / NodeJS / Python / Etc !
- 8.3. devdocs
- 8.4. How do I do something?
- 8.5. Sleek Semantic Selection
- 8.6. Managing Processes/Servers from within Emacs —Work-specific functions
- 8.7. Project management & navigation
- 8.8. TODO Projectile
- 8.9. TODO Are there any errors in my code?
- 8.10. Jumping to definitions & references
- 8.11. Documentation Pop-Ups
- 8.12. Turbolog: What's the value of this expression? JavaScript
- 8.13. Which function are we writing?
- 8.14. Highlight defined Lisp symbols
- 8.15. Being Generous with Whitespace
- 8.16. Coding with a Fruit Salad: Semantic Highlighting
- 8.17. Jump between windows using Cmd+Arrow & between recent buffers with Meta-Tab relocate
- 8.18. Shell / Terminal relocate
- 8.19. Github Browser Extensions (for Chrome)
- 8.20. Browse remote files
- 8.21. A nice Emacs interface for a portion of the “gh” CLI
- 8.22. copy-as-format: Emacs function to copy buffer locations as GitHub/Slack/JIRA etc… formatted code.
- 8.23. See all company related PRs
- 8.24. SQL —via LSP
- 8.25. Docker
- 8.26. my/open-in-terminal '⌘
- 8.27. Check if application APP is currently running, in use.
- 8.28. LSP: Making Emacs into a generic full-featured programming IDE
- 8.29. JSON
- 8.30. w-screencapture
- 8.31. Screencapturing the Current Emacs Frame
- 8.32. Comment-boxes up to the fill-column
- 8.33. Auto-format on Save
- 8.34. Searching Hydra
- 8.35. Peer Review / Pull Request Template for Work
- 9. Web-Development
- 10. Lisp Helpers / Kill all buffers that are not associated with a file
- 11. Toggles Hydra Not_yet_tangled
- 12. Lost Souls Outdated_Documentation Not_yet_tangled
- 12.1. Note: M-S-SPC is for my personal servers dashboard.
- 12.2. Zoom
- 12.3. Ibuffer
- 12.4. find function at point
- 12.5. “C-x 2” and “C-x 3” now create a new window horizontally/vertically and send cursor there
- 12.6. Semantic Change
- 12.7. Drag Stuff Disabled
- 12.8. Indentation Guide
- 12.9. Commenting
- 12.10. Having a workspace manager in Emacs
- 12.11. Editor Documentation with Contextual Information
- 12.12.
README
—Frominit.org
toinit.el
- 12.13. Org-mode's
<𝒳
Block Expansions - 12.14. What's changed & who's to blame?
- 12.15. Emacs keybindings for my browser Disabled
- 12.16. Using Emacs in any text area on my OS Disabled
- 12.17. Reload buffer with
f5
- 12.18. Kill to start of line
- 12.19. Killing buffers & windows:
C-x k
has a family - 12.20. Switching from 2 horizontal windows to 2 vertical windows
- 12.21. Obtaining Values of
#+KEYWORD
Annotations - 12.22. Publishing articles to my personal blog
- 12.23. Jumping without hassle
- 12.24. The evils of this world
- 12.25. TODO Hydra Timer
- 12.26. Makes Org/Markdown previewabvle as we type!!! ♥
- 12.27. Draw pretty unicode tables in org-mode to_include
- 12.28. Cucumber
- 12.29. Syntax highlighting —numbers and escape characters
- 12.30. shell-command-and-run
- 12.31. Keeping my system up to date
- 12.32. Compile posterity
- 12.33. Let's jump to a current Chrome browser tab, or one from our Chrome history, from within Emacs.
- 12.34. Get Shell history within Emacs via Completing Read with Helm
- 12.35. Launch macOS apps with Helm
- 12.36. Use Org Mode links in other modes: Links can be opened and edited like in Org Mode.
- 12.37. Let's make working with Emacs Lisp even better!
- 12.38. A butler for your buffers. Group buffers into workspaces with programmable rules, and easily switch to and manipulate them.
- 12.39. Let's try out this dope theme and this one too!
- 13. Conclusion —Why Configuration Files Should be Literate
Abstract
Hello! Herein I document the configurations I utilise with Emacs.
As a literate program file with Org-mode, I am ensured optimal navigation through my ever growing configuration files, ease of usability and reference for peers, and, most importantly, better maintainability for myself!
Dear reader, when encountering a foregin command X
I encourage you to execute
(describe-symbol 'X)
, or press C-h o with the cursor on X
. An elementary
Elisp Cheat Sheet can be found at and
is a 2-page 3-column PDF of the bindings in this configuration.
- C-h e ⇒ What'd Emacs do?
- C-h o ⇒ What's this thing?
- C-h l ⇒ What'd I do?
- C-h ? ⇒ What're the help topics? —gives possible completions to “C-h ⋯”.
- “I accidentally hit a key, which one and what did it do!?” ⇒ C-h e and C-h l, then use C-h o to get more details on the action. ;-)
Finally, C-h d asks nicely what ‘d’ocumentation you're interested in.
After providing a few keywords, the apropos
tool yields possible functions
and variables that may accomplish my goal.
This article is about how I like to do things ---I'm not insisting others should do things this way.
Always remember that to argue, and win, is to break down the reality of the person you are arguing against. It is painful to lose your reality, so be kind, even if you are right. - Haruki Murakami
Life is too short to not read the very best book you know of right now. - Patrick Collison
Inspiration is for amateurs. The rest of us just show up and get to work. - Chuck Close
“Personal instructions for a new machine”
These steps must be performed at the terminal since they are required to get my Emacs, which then installs everything else when it's first opened.
Install a package manager: https://brew.sh/ :
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Also: Change to the conventional scrolling direction: If I pull my scroll down, I want to go down.
- Apple menu → System Preferences → Mouse → Tick the scroll direction option.
-
brew install --cask emacs
If that fails, try this to install Emacs:
brew tap daviderestivo/emacs-head brew install emacs-head
Then make the command
emacs
available via the terminal —required if doing any melpa development.ln -s /usr/local/opt/emacs-head@27/Emacs.app /Applications sudo ln -s /usr/local/opt/emacs-head@27/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs
- 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 helm-do-grep-ag, useful for finding definitions), Amethyst window manager.
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.
For convenience, on a Mac, add the home (~/
) directory to the default file
navigator: Finder → Preferences → Sidebar, then select home 🏠.
If you notice any “file system access” concerns, give Emacs permissions to read
your files: General Settings → Security & Privacy → Full Disk Access → ⌘-M-g
(to
search) then enter /usr/bin/ruby
—Emacs is launched via a Ruby script in
MacOS.
1. Why Emacs?
A raw code file is difficult to maintain, especially for a large system such as Emacs. Instead, we're going with a ‘literate programming’ approach: The intialisation configuration is presented in an essay format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using org-mode —which is Emacs' killer app.
Super Simple Intro to Emacs’ Org-mode
Emacs’ Org-mode is an outliner, a rich markup language, spreadsheet tool, literate programming system, and so much more. It is an impressive reason to use Emacs (•̀ᴗ•́)و
Org-mode syntax is very natural; e.g., the following is Org-mode! ( Org Mode Is One of the Most Reasonable Markup Languages to Use for Text )
+ Numbered and bulleted lists are as expected. - Do the things: 1. This first 2. This second 44. [@44] This forty-fourth - =[@𝓃]= at the beginning of an iterm forces list numbering to start at 𝓃 - =[ ]= or =[X]= at the beginning for checkbox lists - Use =Alt ↑, ↓= to move items up and down lists; renumbering happens automatically. + Definitions lists: ~- term :: def~ + Use a comment, such as =# separator=, between two lists to communicate that these are two lists that /happen/ to be one after the other. Or use any /non-indented/ text to split a list into two. * My top heading, section words ** Child heading, subsection more words *** Grandchild heading, subsubsection even more!
Export In Emacs, press C-c C-e h o to obtain an HTML webpage ---like this one!— of the Org-mode markup; use C-c C-e l o to obtain a PDF rendition.
You can try Org-mode notation and see how it renders live at: http://mooz.github.io/org-js/
You make a heading by writing * heading
at the start of a line, then you can
TAB to fold/unfold its contents. A table of contents, figures, tables can be
requested as follows:
# figures not implemented in the HTML backend # The 𝓃 is optional and denotes headline depth #+toc: headlines 𝓃 #+toc: figures #+toc: tables
Markup elements can be nested.
Syntax Result /Emphasise/
, italicsEmphasise *Strong*
, boldStrong */very strongly/*
, bold italicsvery 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
provides support for numerous other kinds of markup elements, such as
red:hello
which becomes “ hello ”.
Working with tables
#+ATTR_HTML: :width 100% #+name: my-tbl #+caption: Example table | Who? | What? | |------+-------| | me | Emacs | | you | Org |
Note the horizontal rule makes a header row and is formed by typing doit then pressing TAB. You can TAB between cells.
- You can make an empty table with
C-c |
, which is just org-table-create-or-convert-from-region, then give it row×column dimensions. - Any lines with comma-separated-values (CSV) can be turned into an Org table by
selecting the region and pressing
C-u C-c |
. (Any CSV file can thus be visualised nicely as an Org table). - Use
C-u C-u C-u C-c |
to make a table from values that are speared by a certain regular expression.
Working with links
Link syntax is [[source url][description]]
; e.g., we can refer to the above
table with [[my-tbl][woah]]
.
Likewise for images: file:path-to-image.
Mathematics
Source
\[ \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \]
Result
\[ \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \]
- Instead of
\[...\]
, which displays a formula on its own line, centred, use$...$
to show a formula inline. - Captioned equations are numbered and can be referenced via links, as shown below.
Source
#+name: euler \begin{equation} e ^ {i \pi} + 1 = 0 \end{equation} See equation [[euler]].
Result
\begin{equation} \label{orgbefd1a4} e ^ {i \pi} + 1 = 0 \end{equation}See equation \eqref{orgbefd1a4}.
Source code
Source
#+BEGIN_SRC C -n int tot = 1; (ref:start) for (int i = 0; i != 10; i++) (ref:loop) tot *= i; (ref:next) printf("The factorial of 10 is %d", tot); #+END_SRC
Result
1: int tot = 1; (start) 2: for (int i = 0; i != 10; i++) (loop) 3: tot *= i; (next) 4: printf("The factorial of 10 is %d", tot);
The labels (ref:name)
refer to the lines in the source code and can be
referenced with link syntax: [[(name)]]
. Hovering over the link, in the HTML
export, will dynamically highlight the corresponding line of code. To strip-out
the labels from the displayed block, use -r -n
in the header so it becomes
#+begin_src C -r -n
, now the references become line numbers.
Another reason to use Org:
If you use :results raw
, you obtain dynamic templates that may use Org-markup:
Source
#+BEGIN_SRC C printf("*bold* +%d+ (strikethrough) /slanted/", 12345); #+END_SRC ♯+RESULTS: *bold* +12345+ (strikethrough) /slanted/
Result
printf("*bold* +%d+ (strikethrough) /slanted/", 12345);
♯+RESULTS:
bold 12345 (strikethrough) slanted
The #+RESULTS:
is obtained by pressing C-c C-c on the src
block, to execute
it and obtain its result.
Also: Notice that a C program can be run without a main
;-)
That is, we can write code in between prose that is intended to be read like an essay:
Single source of truth: This mini-tutorial can be included into other Org files by declaring
#+include: ~/.emacs.d/init.org::#Mini-tutorial-on-Org-mode |
For more, see https://orgmode.org/features.html.
Emacs is a flexible platform for developing end-user applications —unfortunately it is generally perceived as merely a text editor. Some people use it specifically for one or two applications.
For example, writers use it as an interface for Org-mode and others use it as an interface for version control with Magit. Org is an organisation tool that can be used for typesetting which subsumes LaTeX, generating many different formats –html, latex, pdf, etc– from a single source, keeping track of schedules & task management, blogging, habit tracking, personal information management tool, and much more. Moreover, its syntax is so natural that most people use it without even knowing! For me, Org allows me to do literate programming: I can program and document at the same time, with no need to seperate the two tasks and with the ability to generate multiple formats and files from a single file.
A list of programs that can be replaced by Emacs
Pieces of (disparate) software can generally be replaced by (applications written on the) Emacs (text processing Lisp platform).
From the table below, of non-editing things you can do with Emacs, it's reasonable to think of Emacs as an operating system —and Vim/Evil is one of its text editors.
Application | ≈ | Emacs Package |
---|---|---|
Habit Tracker / TODO-list | Org mode | |
Agenda / Calendar / Time Tracker | Org mode | |
Literate Programming (like Jupyter) | Org mode | |
Blogging Software | Org mode | |
Reference Information Platform | Org mode with refile and my/reference | |
Word Processing / PDFs / Slidedeck tool | Org mode | |
Spell checker & dictionary & grammar checker | ispell & langtool | |
Reference and citation manager | org-ref | |
PDF Viewer | PDF View mode | |
Powerful Calculator | Calc-mode (Nice article on literate calc mode) | |
Fillable Forms / Data Entry | Widgets | |
Ebook Reader | nov.el and calibredb.el | |
Git / Version control | Magit or vc-mode | |
Shells | eshell or shell | |
Terminal emulators | term, ansi-term, and vterm | |
Package Manager | helm-system-packages | |
File Manager | dired | |
IDE / debugger | LSP / Dap | |
Scripting Language | Emacs Lisp | |
[Neo]Vim / Modal text editor | EVIL mode | |
RSS Newsreader | ElFeed | |
Gnus / Mu4e [very pretty!] / notmuch | ||
Spredsheet tool | Org Table / Simple Emacs Spreadsheet / spreadsheet-mode / csv-mode | |
Automatic file backups | ⟨Built-in⟩ & backup-walker | |
seemless GPG tool | ⟨Built-in⟩ | |
Lisp interpreter | Anywhere press C-x C-e to run a Lisp expression | |
Documentation viewer | tldr-mode; C-h o / describe-symbol | |
Diff / Merge tool | ediff, diff | |
Games | tetris, pacman, mario, etc | |
Psychologist | doctor | |
Typing tutor | typing-of-emacs | |
Modern Internet Browser | xwidget-webkit-browse-url | |
everything else | EAF |
I’m down to essentially Emacs and Chrome for almost all my work —I like using Chrome; I like the integration of all things Google.
- The Nyxt browser is an eerily Emacs-like browser ;-)
Were I “only coding”, then I'd use a popular Integrated Development Environment that requires minimal setup and Just Worksᵀᴹ; but I blog, make cheat sheets, run background services, etc, and so I need an Integrated Computing Environment: Emacs.
If you are a professional writer…Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish. —Neal Stephenson, In the beginning was the command line
Extensible ⇒ IDEs are generally optimised for one framework, unlike Emacs!
- You can program Emacs to automate anything you want.
- Hence, it's an environment, not just an editor.
- ⇒ Unified keybinding across all tools in your environment.
Users are given a high-level full-featured programming language, not just a small configuration language. For the non-programmers, there is Custom, a friendly point-and-click customisation interface.
- Self Documented ⇒ Simply M-x info-apropos or C-h d to search all manuals or look up any function provided by Emacs!
- Mature ⇒ tool with over 40 years of user created features
- Plugins for nearly everything!
- No distinction between built-ins and user-defined features! (Lisp!)
- You can alter others' code without even touching the source.
- Advising functions and ‘hooking’ functionality onto events.
- Free software ⇒ It will never die!
- Emacs is one of the oldest open source projects still under developement.
Of course Emacs comes with the basic features of a text editor, but it is much more; for example, it comes with a powerful notion of ‘undo’: Basic text editors have a single stream of undo, yet in Emacs, we have a tree –when we undo and make new edits, we branch off in our editing stream as if our text was being version controlled as we type! –We can even switch between such branches!
;; Allow tree-semantics for undo operations. (use-package undo-tree :diminish ;; Don't show an icon in the modeline :bind ("C-x u" . undo-tree-visualize) :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME. :config ;; Always have it on (global-undo-tree-mode) ;; Each node in the undo tree should have a timestamp. (setq undo-tree-visualizer-timestamps t) ;; Show a diff window displaying changes between undo nodes. (setq undo-tree-visualizer-diff t)) ;; Execute (undo-tree-visualize) then navigate along the tree to witness ;; changes being made to your file live!
( The above snippet has a noweb-ref
: It is presented here in a natural
position, but is only executable once use-package
is setup and so it
is weaved there! We can present code in any order and tangle it to
the order the compilers need it to be! )
Emacs is an extensible editor: You can make it into the editor of your dreams!
You can make it suited to your personal needs.
If there's a feature you would like, a behaviour your desire, you can simply code that into Emacs with
a bit of Lisp. As a programming language enthusiast, for me Emacs is my default Lisp interpreter
and a customisable IDE that I use for other programming languages
–such as C, Haskell, Agda, Lisp, and Prolog.
Moreover, being a Lisp interpreter, we can alter the look and feel of Emacs live, without having
to restart it –e.g., press C-x C-e after the final parenthesis of (scroll-bar-mode 0)
to run the code that removes the scroll-bar.
I use Emacs every day. I rarely notice it. But when I do, it usually brings me joy. ─Norman Walsh
I have used Emacs as an interface for developing cheat sheets, for making my blog, and as an application for ‘interactively learning C’. If anything Emacs is more like an OS than just a text editor –“living within Emacs” provides an abstraction over whatever operating system my machine has: It's so easy to take everything with me. Moreover, the desire to mould Emacs to my needs has made me a better programmer: I am now a more literate programmer and, due to Elisp's documentation-oriented nature, I actually take the time and effort to make meaningful documentation –even when the project is private and will likely only be seen by me.
Seeing Emacs as an editor is like seeing a car as a seating-accommodation. – Karl Voit
1.1. Emacs is a flexible platform for developing end-user applications
Just as a web browser is utilised as a platform for deploying applications, or ‘extensions’, written in JavaScript that act on HTML documents, Emacs is a platform for deploying applications written in Emacs Lisp that act on buffers of text. In the same vein, people who say Emacs having Tetris is bloat are akin to non-coders who think their browser has bloat since it has a “view page source” feature —which nearly all browsers have, yet it's only useful to web developers. Unlike a web browser in which the user must get accustomed to its features, Emacs is customised to meet the needs of its user. ( Incidentally, Emacs comes bundled with a web browser. )
In the case of Emacs the boundary between user and programmer is blurred as adapting the environment to one’s needs is already an act of programming with a very low barrier to entry. ---rekado
Don't just get used to your tool, make it get used to you!
Emacs is not just an editor, but a host for running Lisp applications!
For example, Emacs is shipped as a language-specific IDE to a number of communities —e.g., Oz, Common Lisp, and, most notably, Agda. Emacs is a great IDE for a language —one just needs to provide a ‘major mode’ and will then have syntax highlighting, code compleition, jumping to definitions, etc. There is no need to make an IDE from scratch.
1.2. The Power of Text Manipulation
Emacs has ways to represent all kinds of information as text.
E.g., if want to make a regular expression rename of files in a directory, there's no need to learn about a batch renaming tool: M-x dired ⟨RET⟩ M-x wdired-change-to-wdired-mode now simply perform a usual find-and-replace, then save with the usual C-x C-s to effect the changes!
Likewise for other system utilities and services (•̀ᴗ•́)و
Moreover, as will be shown below, you can literally use Emacs anywhere for textually input in your operating system –no copy-paste required.
1.4. Emacs Proverbs as Koan
Below is an extract from William Cobb's “Reflections on the Game of Go”, with minor personalised adjustements for Emacs. Enjoy!
The Japanese term satori refers to the experience of enlightenment, the realisation of how things really are that is the primary aim of practice and meditation. However, the Zen tradition is famous for claiming that one cannot say what it is that one realises, that is, one cannot articulate the content of the enlightenment experience. Although it makes everything clear, it is an experience beyond words. Instead of being given an explanation of how things are, the student of Zen hears sayings called koan, often somewhat paradoxical in character, that come from those who are enlightened:
- “There are no CTRL and META.”
- “If you meet an Emacs you dislike, kill it.”
- “No one knows Emacs.”
- “One can only learn Emacs by living within it.”
- “To know Org mode is to know oneself.”
It is important to realise that koan are intended to move you off of one path and onto another. They are not just attempts to mystify you. For example, the first proverb is in regards to newcomers complaining about too many keybinings —eventually it's muscle memory—, whereas the second is about using the right tool for the right task —Emacs is not for everyone. The fourth is, well, Emacs is an operating system.
1.5. Possibly Interesting Reads
- 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.
I’m stunned that you, as a professional software engineer, would eschew inferior computer languages that hinder your ability to craft code, but you put up with editors that bind your fingers to someone else’s accepted practice. ---Howard Abrams
1.6. Fun commands to try out
Finally, here's some fun commands to try out:
M-x doctor
—generalising the idea of rubber 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…
1.7. What Does Literate Programming Look Like?
Briefly put, literate programming in Emacs allows us to evaluate source code within our text files, then using the results as values in other source blocks. When presenting an algorithm, we can talk it out, with a full commentary thereby providing ‘reproducible research’: Explorations and resulting algorithms are presented together in a natural style.
⟨ This image was created in org-mode; details below or by looking at the source file 😉 ⟩
Here's an example of showing code in a natural style, but having the resulting code appear in a style amicable to a machine. Here's what you type:
It's natural to decompose large problems, #+begin_src haskell :noweb-ref defn-of-f :tangle no f = h ∘ g #+end_src But we need to define $g$ and $h$ /beforehand/ before we can use them. Yet it's natural to “motivate” their definitions ---rather than pull a rabbit out of hat. Org lets us do that! Here's one definition, #+begin_src haskell :noweb-ref code-from-other-places :tangle no g = ⋯ #+end_src then the other. #+begin_src haskell :noweb-ref code-from-other-places :tangle no h = ⋯ #+end_src Of-course, we might also want a preamble: #+BEGIN_SRC haskell :tangle myprogram.hs import ⋯ #+END_SRC We can now tangle together the tagged code blocks in the order we want. #+BEGIN_SRC haskell :tangle myprogram.hs :comments none :noweb yes <<code-from-other-places>> <<defn-of-f>> #+END_SRC ( You can press “C-c C-v C-v” to see what this block expands into! )
Now C-c C-v C-t (org-babel-tangle) yields a file named myprogram.hs
with contents in an order
amicable to a machine.
import ⋯ g = ⋯ h = ⋯ f = h ∘ g
Interestingly, unlike certain languages, Haskell doesn't care too much about declaration order.
Warning! If we have different language blocks tangled to the same file, then
they are tangled alphabetically —e.g., if one of the blocks is marked
emacs-lisp
then its contents will be the very first one in the resulting source
file, since emacs-lisp
begins with e
which is alphabetically before h
of
haskell
.
1.8. Why a monolithic configuration?
Why am I keeping my entire configuration —from those involving cosmetics &
prose to those of agendas & programming— in one file? Being monolithic —“a
large, mountain-sized, indivisible block of stone”— is generally not ideal in
nearly any project: E.g., a book is split into chapters and a piece of software
is partitioned into modules. Using Org-mode, we can still partition our setup
while remaining in one file. An Emacs configuration is a personal, leisurely
project, and one file is a simple architecture: I don't have to worry about many
files and the troubles of moving content between them; instead, I have headings
and move content almost instantaneously —org-refile by pressing w
at the start
of the reader. Moreover, being one file, it is easy to distribute and to extract
artefacts from it —such as the README for Github, the HTML for my blog, the
colourful PDF rendition, and the all-important Emacs Lisp raw code
file. Moreover, with a single #
I can quickly comment out whole sections,
thereby momentarily disabling features.
There's no point in being modular if there's nothing explaining what's going on, so I document.
The section of this read further argues the benefits of maintaining
literate, and monolithic, configuration files. As a convention, I will try to
motivate the features I set up and I will prefix my local functions with, well,
my/
—this way it's easy to see all my defined functions, and this way I cannot
accidentally shadow existing utilities. Moreover, besides browsing the web, I do
nearly everything in Emacs and so the start-up time is unimportant to me: Once
begun, I have no intention of spawning another instance nor closing the current
one. ( Upon an initial startup using this configuration, it takes a total of
121 seconds to install all the packages featured here. )
Enjoy!
2. Booting Up
Let's decide on where we want to setup our declarations for personalising Emacs to our needs. Then, let's bootstrap Emacs' primintive packaging mechanism with a slick interface —which not only installs Emacs packages but also programs at the operating system level, all from inside Emacs! Finally, let's declare who we are and use that to setup Emacs email service.
2.1. ~/.emacs
vs. init.org
Emacs is extensible: When Emacs is started, it tries to load a user's Lisp
program known as an initialisation (‘init’) file which specifies how Emacs
should look and behave for you. Emacs looks for the init file using the
filenames ~/.emacs.el
, ~/.emacs
, or ~/.emacs.d/init.el
—it looks for the first
one that exists, in that order; at least it does so on my machine. Below we'll
avoid any confusion by ensuring that only one of them is in our system.
Regardless, execute C-h o user-init-file to see the name of the init file
loaded. Having no init file is tantamount to have an empty init file.
- One can read about the various Emacs initialisation files online or within Emacs by the sequence C-h i m emacs RET i init file RET.
- A friendly tutorial on ‘beginning a
.emacs
file’ can be read online or within Emacs by C-h i m emacs lisp intro RET i .emacs RET. - After inserting some lisp code, such as
(set-background-color "salmon")
, and saving, one can load the changes with M-x eval-buffer, eval-buffer. - In a terminal, use
emacs -Q
to open emacs without any initialisation files.
Besides writing Lisp in an init file, one may use Emacs' customisation
interface, M-x customize: Point and click to change Emacs to your needs. The
resulting customisations are, by default, automatically thrown into your init
file ---~/.emacs
is created for you if you have no init file. This interface is
great for beginners.
We shall use ~/.emacs.d/init.el
as the initialisation file so that all of our
Emacs related files live in the same directory: ~/.emacs.d/
.
A raw code file is difficult to maintain, especially for a large system such as Emacs. Instead, we're going with a ‘literate programming’ approach: The intialisation configuration is presented in an essay format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using org-mode ---Emacs' killer app— which is discussed in great detail later on.
/Adventure time!/ “Honey, where's my init?”
Let's use the three possible locations for the initialisation files to explore how Emacs finds them. Make the following three files.
~/.emacs.el
;; Emacs looks for this first; (set-background-color "chocolate3") (message-box ".emacs.el says hello")
~/.emacs
;; else; looks for this one; (set-background-color "plum4") (message-box ".emacs says hello")
~/.emacs.d/init.el
;; Finally, if neither are found; it looks for this one. (set-background-color "salmon") (message-box ".emacs.d/init.el says hello")
Now restart your Emacs to see how there super tiny initilaisation files affect your editor. Delete some of these files in-order for others to take effect!
Adventure time! Using Emacs’ Easy Customisation Interface
~/.emacs
since
Emacs may explicitly add, or alter, code in it.
Let's see this in action!
Execute the following to see additions to the ~/.emacs
have been added by
‘custom’.
- M-x customize-variable RET line-number-mode RET
- Then press: toggle, state, then 1.
- Now take a look: C-x C-f ~/.emacs
Support for ‘Custom’
README.org
's and other matters, so Emacs' Custom utility
will remember to not prompt me each time for the safety of such local variables.
(setq custom-file "~/.emacs.d/custom.el") (ignore-errors (load custom-file)) ;; It may not yet exist.
2.2. Who am I?
Let's set the following personal Emacs-wide variables —to be used locations such as email.
(setq user-full-name "Musa Al-hassy" user-mail-address "alhassy@gmail.com")
For some fun, run this cute method.
(animate-birthday-present user-full-name)
2.3. Emacs Package Manager
There are a few ways to install packages —run C-h C-e for a short overview. The easiest, for a beginner, is to use the command package-list-packages then find the desired package, press i to mark it for installation, then install all marked packages by pressing x.
- Interactively: M-x list-packages to see all melpa packages that can install
- Press Enter on a package to see its description.
- Or more quickly, to install, say, unicode fonts: M-x package-install RET unicode-fonts RET.
“From rags to riches”: Recently I switched to Mac —first time trying the OS.
I had to do a few package-install
's and it was annoying. I'm looking for the
best way to package my Emacs installation —including my installed packages and
configuration— so that I can quickly install it anywhere, say if I go to
another machine. It seems use-package allows me to configure and auto
install packages. On a new machine, when I clone my .emacs.d
and start Emacs,
on the first start it should automatically install and compile all of my
packages through use-package
when it detects they're missing. ♥‿♥
First we load package
, the built-in package manager. It is by default only
connected to the GNU ELPA (Emacs Lisp Package Archive) repository, so we
extended it with other popular repositories; such as the much larger MELPA
(Milkypostman's ELPA) —it builds packages directly from the source-code
repositories of developers rather than having all packages in one repository.
;; Make all commands of the “package” module present. (require 'package) ;; Internet repositories for new packages. (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/") ("melpa" . "http://melpa.org/packages/"))) ;; Update local list of available packages: ;; Get descriptions of all configured ELPA packages, ;; and make them available for download. (package-refresh-contents)
- All installed packages are placed, by default, in
~/.emacs.d/elpa
. - Neato: If one module requires others to run, they will be installed automatically.
The declarative configuration tool use-package is a macro/interface that manages our packages and the way they interact.
(unless (package-installed-p 'use-package) (package-install 'use-package)) (require 'use-package)
We can now invoke (use-package XYZ :ensure t)
which should check for the XYZ
package and makes sure it is accessible. If the file is not on our system, the
:ensure t
part tells use-package
to download it —using the built-in package
manager— and place it somewhere accessible, in ~/.emacs.d/elpa/
by default.
By default we would like to download packages, since I do not plan on installing
them manually by downloading Lisp files and placing them in the correct places
on my system.
(setq use-package-always-ensure t)
Notice that use-package allows us to tersely organise a package's
configuration —and that it is not a package manger, but we can make it one by
having it automatically install modules, when needed, using :ensure t
.
Super Simple ‘use-package’ Mini-tutorial
Here are common keywords we will use, in super simplified terms.
:init f₁ … fₙ
Always executes code formsfᵢ
before loading a package.:diminish str
Uses optional stringstr
in the modeline to indicate this module is active. Things we use often needn't take real-estate down there and so no we provide nostr
.:config f₁ … fₙ
Only executes code formsfᵢ
after loading a package.The remaining keywords only take affect after a module loads.
:bind ((k₁ . f₁) … (kₙ . fₙ)
Lets us bind keyskᵢ
, such as"M-s o"
, to functions, such asoccur
.- When n = 1, the extra outer parenthesis are not necessary.
:hook ((m₁ … mₙ) . f)
Enables functionalityf
whenever we're in one of the modesmᵢ
, such asorg-mode
. The. f
, along with the outermost parenthesis, is optional and defaults to the name of the package —Warning: Erroneous behaviour happens if the package's name is not a function provided by the package; a common case is when package's name does not end in-mode
, leading to the invocation((m₁ … mₙ) . <whatever-the-name-is>-mode)
instead.Additionally, when n = 1, the extra outer parenthesis are not necessary.
Outside of
use-package
, one normally uses aadd-hook
clause. Likewise, an ‘advice’ can be given to a function to make it behave differently —this is known as ‘decoration’ or an ‘attribute’ in other languages.:custom (k₁ v₁ d₁) … (kₙ vₙ dₙ)
Sets a package's custom variableskᵢ
to have valuesvᵢ
, along with optional user documentationdᵢ
to explain to yourself, in the future, why you've made this decision.This is essentially
setq
within:config
.- Use the standalone keyword
:disabled
to turn off loading a module that, say, you're not using anymore.
We now bootstrap use-package
.
The use of :ensure t
only installs absent modules, but it does no updating.
Let's set up an auto-update mechanism.
(use-package auto-package-update :config ;; Delete residual old versions (setq auto-package-update-delete-old-versions t) ;; Do not bother me when updates have taken place. (setq auto-package-update-hide-results t) ;; Update installed packages at startup if there is an update pending. (auto-package-update-maybe))
Here's another example use of use-package
. Later on, I have a “show recent files
pop-up” command set to C-x C-r
; but what if I forget? This mode shows me all key
completions when I type C-x
, for example. Moreover, I will be shown other
commands I did not know about! Neato :-)
;; Making it easier to discover Emacs key presses. (use-package which-key :diminish :config (which-key-mode) (which-key-setup-side-window-bottom) (setq which-key-idle-delay 0.05))
⟨ Honestly, I seldom even acknowledge this pop-up; but it's always nice to show to people when I'm promoting Emacs. ⟩
Above, the :diminish
keyword indicates that we do not want the mode's name to be
shown to us in the modeline —the area near the bottom of Emacs. It does so by
using the diminish
package, so let's install that.
(use-package diminish)
Here are other packages that I want to be installed onto my machine.
;; Haskell's cool (use-package haskell-mode :defer t) ;; Lisp libraries with Haskell-like naming. (use-package dash) ;; “A modern list library for Emacs” (use-package s ) ;; “The long lost Emacs string manipulation library”. ;; Let's use the “s” library. (defvar my/personal-machine? (not (s-contains? "work-machine" (shell-command-to-string "scutil --get ComputerName"))) "Is this my personal machine, or my work machine? At one point, on my work machine I run the following command to give the machine a sensible name. sudo scutil --set ComputerName work-machine dscacheutil -flushcache") (defvar my/work-machine? (not my/personal-machine?)) ;; Library for working with system files; ;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden? (use-package f)
Note:
- dash: “A modern list library for Emacs”
- E.g.,
(--filter (> it 10) (list 8 9 10 11 12))
- 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.
;; Allow tree-semantics for undo operations. (use-package undo-tree :diminish ;; Don't show an icon in the modeline :bind ("C-x u" . undo-tree-visualize) :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME. :config ;; Always have it on (global-undo-tree-mode) ;; Each node in the undo tree should have a timestamp. (setq undo-tree-visualizer-timestamps t) ;; Show a diff window displaying changes between undo nodes. (setq undo-tree-visualizer-diff t)) ;; Execute (undo-tree-visualize) then navigate along the tree to witness ;; changes being made to your file live!
DRY: Don 't Repeat Yourself!
In the HTML export, above it looks like I just copy-pasted the undo tree setup from earlier, but that is not the case! All I did was declare to Org that I'd like that named snippet to be tangled now, here in the resulting code file.
#+begin_src emacs-lisp :noweb yes <<undo-tree-setup>> #+end_src
You can press C-c C-v C-v, org-babel-expand-src-block, to see what this block expands into —which is what was shown above.
Quelpa allows us to build Emacs packages directly from source repositories. It
derives its name from the German word Quelle, for souce [code], adjoined to
ELPA. Its use-package
interface allows us to use use-package
like normal but
when we want to install a file from souce we use the keyword :quelpa
.
(use-package quelpa :custom (quelpa-upgrade-p t "Always try to update packages") :config ;; Get ‘quelpa-use-package’ via ‘quelpa’ (quelpa '(quelpa-use-package :fetcher git :url "https://github.com/quelpa/quelpa-use-package.git")) (require 'quelpa-use-package))
Let's use this to obtain an improved info-mode from the EmacsWiki. [Disabled for now]
(use-package info+ :disabled :quelpa (info+ :fetcher wiki :url "https://www.emacswiki.org/emacs/info%2b.el"))
2.4. Installing OS packages, and automatically keeping my system up to data, from within Emacs
Sometimes Emacs packages depend on existing system binaries, use-package
let's
us ensure these exist using the :ensure-system-package
keyword extension.
- This is like
:ensure t
but operates at the OS level and uses your default OS package manager. - It has multiple features.
Let's obtain the extension.
;; Auto installing OS system packages (use-package use-package-ensure-system-package :config (system-packages-update)) ;; Please don't bother me when shell buffer names are in use, just make a new ;; buffer. (setq async-shell-command-buffer 'new-buffer) ;; Display the output buffer for asynchronous shell commands only when the ;; command generates output. (setq async-shell-command-display-buffer nil) ;; Don't ask me if I want to kill a buffer with a live process attached to it; ;; just kill it please. (setq kill-buffer-query-functions (remq 'process-kill-buffer-query-function kill-buffer-query-functions)) ;; Ensure our operating system is always up to date. ;; This is run whenever we open Emacs & so wont take long if we're up to date. ;; It happens in the background ^_^ ;; ;; After 5 seconds of being idle, after starting up. (defvar my/installed-packages (shell-command-to-string "brew list") "What is on my machine already? Sometimes when I install a GUI based application and do not have access to it everywhere in my path, it may seem that I do not have that application installed. For instance, (system-packages-package-installed-p \"google-chrome\") returns nil, even though Google Chrome is on my machine. As such, we advise the `system-packages-ensure' installtion method to only do installs of pacakges that are not in our `my/installed-packages' listing. ") (advice-add 'system-packages-ensure :before-until (lambda (pkg) (s-contains-p pkg my/installed-packages)))
After an update to Mac OS, one may need to restore file system access privileges to Emacs.
Here's an example use for Emacs packages that require OS packages:
(shell-command-to-string "type rg") ;; ⇒ rg not found (use-package rg :ensure-system-package rg) ;; ⇒ There's a buffer *system-packages* ;; installing this tool at the OS level!
If you look at the *Messages*
buffer, via C-h e
, on my machine it says
brew install rg: finished
—it uses brew
which is my OS package manager!
- The 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. (when (eq system-type 'darwin) (setq system-packages-package-manager 'brew)) ;; If the given system package doesn't exist; install it. (when (eq system-type 'darwin) (system-packages-ensure "amethyst")) ;; This is a MacOS specific package. (ignore-errors (system-packages-ensure "google-chrome")) ;; My choice of web browser (system-packages-ensure "microsoft-teams") ;; For remote work meetings ;; Gif maker; needs privileges to capture screen. ;; ;; ⇒ Move the screen capture frame while recording. ;; ⇒ Pause and restart recording, with optional inserted text messages. ;; ⇒ Global hotkey (shift+space) to toggle pausing while recording (system-packages-ensure "licecap") ;; Use: ⌘-SPACE licecap ;; Pack, ship and run any application as a lightweight container (system-packages-ensure "docker") ;; Free universal database tool and SQL client (system-packages-ensure "dbeaver-community") ;; Kubernetes IDE (system-packages-ensure "lens") ;; Platform built on V8 to build network applications ;; Also known as: node.js, node@16, nodejs, npm (system-packages-ensure "node") ;; https://nodejs.org/ ;; Nice: https://nodesource.com/blog/an-absolute-beginners-guide-to-using-npm/ ;; Manage multiple Node.js versions (shell-command "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash") ;; According to https://github.com/nvm-sh/nvm, nvm shouldn't be installed via brew.
#+ENDSRC
Amethyst requires some more setup: Open its preferences, then…
- Then select:
Mouse: Focus follows mouse
. - Also:
Shortcuts
, then disable ‘increase/decrease main pane count’ bindings since they override the beloved 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 ⌘-a r relaunches Amethyst.
Details
(defun ⌘-quit (app) "Kill application APP; e.g., “amethyst” or “Safari”" (shell-command (format "osascript -e 'quit app \"%s\"'" app))) (defun ⌘-open (app) "Open application APP; e.g., “amethyst” or “Safari”" (async-shell-command (format "osascript -e 'launch app \"%s\"'" app))) ;; (bind-key "???-a r" #'my/relaunch-amethyst) (defun my/relaunch-amethyst () (interactive) (⌘-quit "amethyst") (⌘-open "amethyst"))
We use the osascript
command to tell
the System Events application
to issue
keystrokes to other applications. I found out about by Googling “How to send
keystrokes from terminal”.
;; (bind-key "???-a c" #'amethyst/cycle-layout) (defun amethyst/cycle-layout () (interactive) (shell-command "osascript -e 'tell application \"System Events\" to keystroke space using {shift down, option down}'"))
If you get:
36:51: execution error: System Events got an error: osascript is not allowed to send keystrokes. (1002)
Then: Go to Security & Privacy -> Privacy tab -> Accessibility -> Add osascript (/usr/bin/osascript)
You may need to restart Emacs.
Reads:
I enter “⌘” using a TeX input method setup below (called “Agda”).
2.5. Syncing to the System's $PATH
For one reason or another, on OS X it seems that an Emacs instance
begun from the terminal may not inherit the terminal's environment
variables, thus making it difficult to use utilities like pdflatex
when Org-mode attempts to produce a PDF.
(use-package exec-path-from-shell :init (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize)))
See the exec-path-from-shell documentation for setting other environment variables.
2.6. Restarting Emacs —Keeping buffers open across sessions?
Sometimes I wish to close then reopen Emacs; unsurprisingly someone's thought of implementing that.
;; Provides only the command “restart-emacs”. (use-package restart-emacs ;; If I ever close Emacs, it's likely because I want to restart it. :bind ("C-x C-c" . restart-emacs) ;; Let's define an alias so there's no need to remember the order. :config (defalias 'emacs-restart #'restart-emacs))
The following is disabled. I found it a nuisance to have my files open across sessions —If I'm closing Emacs, it's for a good reason.
;; Keep open files open across sessions. (desktop-save-mode 1) (setq desktop-restore-eager 10)
Instead, let's try the following: When you visit a file, point goes to the last place where it was when you previously visited the same file.
(setq-default save-place t) (setq save-place-file "~/.emacs.d/etc/saveplace")
2.7. “Being at the Helm” —Completion & Narrowing Framework
Whenever we have a choice to make from a list, Helm provides possible
completions and narrows the list of choices as we type. This is extremely
helpful for when switching between buffers, C-x b
, and discovering & learning
about other commands! E.g., press M-x
to see recently executed commands and
other possible commands! “Fuzzy finding”: Press M-x
and just start typing,
methods mentioning what you've typed are suddenly listed! Moreover, C-c i
(helm-imenu) will show you the headers in an Org file or the top-level
variables/functions/types when programming. Finally, whenever a Helm session has
started, toggle follow-mode with C-c C-f
to obtain contextual-awareness; e.g.,
C-c i RET C-c C-f
will change your screen as you scroll through the menu.
(A killer feature is helm-do-grep-ag which will do a search in your whole project, file tree).
Remembrance comes with time, until then ask Emacs! |
Try and be grateful!
(use-package helm :diminish :init (helm-mode t) :bind (("M-x" . helm-M-x) ("C-x C-f" . helm-find-files) ("C-x b" . helm-mini) ;; See buffers & recent files; more useful. ("C-x r b" . helm-filtered-bookmarks) ("C-x C-r" . helm-recentf) ;; Search for recently edited files ("C-c i" . helm-imenu) ;; C.f. “C-x t m” (imenu-list) ;; ("C-u C-c i" . imenu-list) ;; TODO FIXME Key sequence C-u C-c i starts with non-prefix key C-u ("C-h a" . helm-apropos) ;; Look at what was cut recently & paste it in. ("M-y" . helm-show-kill-ring) ("C-x C-x" . helm-all-mark-rings) :map helm-map ;; We can list ‘actions’ on the currently selected item by C-z. ("C-z" . helm-select-action) ;; Let's keep tab-completetion anyhow. ("TAB" . helm-execute-persistent-action) ("<tab>" . helm-execute-persistent-action))) ;; Show me nice file icons when using, say, “C-x C-f” or “C-x b” (use-package helm-icons :custom (helm-icons-provider 'all-the-icons) :config (helm-icons-enable))
Helm provides generic functions for completions to replace tab-completion in Emacs with no loss of functionality.
The
execute-extended-command
, the default “M-x”, is replaced 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!
Lets also use helm-do-grep-ag (C-x c M-g a) search all files in the current directory for a particular (regexp) string
- Shows matches live as you type
- Very helpful when looking for a definition of something
(system-packages-ensure "ag")
Marking my place when I jump around
Let's use M-m
to get a nice menu of places we've been recently.
;; Save/mark a location with “C-u M-m”, jump back to it with “M-m”. (bind-key* "M-m" (lambda () (interactive) (if (not current-prefix-arg) (helm-mark-ring) (push-mark) (message "[To return to this location, press M-m] ∷ %s" (s-trim (substring-no-properties (thing-at-point 'line)))))))
Finally, note that there is now a M-x helm-info
command to show documentation,
possibly with examples, of the packages installed. For example,
M-x helm-info RET dash RET -parition RET
to see how the parition function from the
dash library works via examples ;-)
;; Make `links' from elisp symbols (quoted functions, variables and fonts) in Gnu-Emacs Info viewer to their help documentation. (use-package inform :config (require 'inform))
2.8. Org-Mode Administrivia
Let's conclude this ‘boot-up’ by getting Emacs' killer app, Org-mode, setup; along with the extras that allow us to ignore heading names, but still utilise their contents —e.g., such as a heading named ‘preamble’ that contains org-mode setup for a file.
(use-package emacs :ensure org-contrib :diminish org-indent-mode :config (require 'ox-extra) (ox-extras-activate '(ignore-headlines)))
org-plus-contrib
contain the files that are included with Emacs plus all
contributions from the org-mode repository.
- Use the
:ignore:
tag on headlines you'd like to have ignored, while not ignoring their content. - Use the
:noexport:
tag to omit a headline and its contents.
;; Replace the content marker, “⋯”, with a nice unicode arrow. (setq org-ellipsis " ⤵") ;; Fold all source blocks on startup. (setq org-hide-block-startup t) ;; Lists may be labelled with letters. (setq org-list-allow-alphabetical t) ;; Avoid accidentally editing folded regions, say by adding text after an Org “⋯”. (setq org-catch-invisible-edits 'show) ;; I use indentation-sensitive programming languages. ;; Tangling should preserve my indentation. (setq org-src-preserve-indentation t) ;; Tab should do indent in code blocks (setq org-src-tab-acts-natively t) ;; Give quote and verse blocks a nice look. (setq org-fontify-quote-and-verse-blocks t) ;; Pressing ENTER on a link should follow it. (setq org-return-follows-link t)
I rarely use tables, but here is a useful Org-Mode Table Editing Cheatsheet and a friendly tutorial.
Moreover, since I end up using org-mode most of the time, let's make that the default mode.
(setq initial-major-mode 'org-mode)
Finally, let's get some extra Org-mode mark-up goodies, such as kbd:C-c_C-e
which renders as C-c C-e. Documentations and screenshots at:
https://alhassy.github.io/org-special-block-extras/
;; TODO org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks ;; (when nil use-package org-special-block-extras :hook (org-mode . org-special-block-extras-mode) :custom ;; The places where I keep my ‘#+documentation’ (org-special-block-extras--docs-libraries '("~/org-special-block-extras/documentation.org")) ;; Disable the in-Emacs fancy-links feature? (org-special-block-extras-fancy-links '(elisp badge kbd link-here doc tweet)) ;; Details heading “flash pink” whenever the user hovers over them? (org-html-head-extra (concat org-html-head-extra "<style> summary:hover {background:pink;} </style>")) ;; The message prefixing a ‘tweet:url’ badge (org-special-block-extras-link-twitter-excitement "This looks super neat (•̀ᴗ•́)و:") :config ;; Use short names like ‘defblock’ instead of the fully qualified name ;; ‘org-special-block-extras--defblock’ (org-special-block-extras-short-names)) ;; Let's execute Lisp code with links, as in “elisp:view-hello-file”. (setq org-confirm-elisp-link-function nil)
2.9. Password-locking files —“encryption”
With the following incantation, we name our files 𝒳.𝒴.gpg
where 𝒳 is the file
name and 𝒴 is the usual extension, then upon save we will be prompted for an
encryption method, we can press Enter on OK
to just provide a password for
that file. You can open that file without the passphrase for a limited amount of
time —i.e., it's cached, saved, for your current computing session until
logout— or force authentication by invoking gpgconf --kill gpg-agent
.
(system-packages-ensure "gnupg") ;; i.e., brew install gnupg ;; “epa” ≈ EasyPG Assistant ;; Need the following in init to have gpg working fine: ;; force Emacs to use its own internal password prompt instead of an external pin entry program. (setq epa-pinentry-mode 'loopback) ;; https://emacs.stackexchange.com/questions/12212/how-to-type-the-password-of-a-gpg-file-only-when-opening-it (setq epa-file-cache-passphrase-for-symmetric-encryption t) ;; No more needing to enter passphrase at each save ^_^ ;; ;; Caches passphrase for the current emacs session?
The purpose of encrypting a file is so that an adversary —e.g., an immoral computer administrator or a thief who stole your computer— will have to spend so much decrypting the data than the data is actually worth. As such, one uses GPG keys…!
GPG Details
Trivia: “gpg” stands for GnuPG, which abbreviates GNU Privacy Guard.
To obtain encrypted messages from others, you will need a “GPG key”: They use your “public key” (which others can see) to encrypt a file, which only you can open since you have the associated “private key” (which only you see).
Possibly interesting reads:
2.10. all-the-icons
(use-package all-the-icons :config (all-the-icons-install-fonts 'install-without-asking)) ;; (cl-defun all-the-icons-faicon (icon &rest _) ;; #("" 0 1 (rear-nonsticky t display (raise -0.24) font-lock-face (:family "FontAwesome" :height 1.2) face (:family "FontAwesome" :height 1.2))))
2.11. Hydra: Supply a prefix only once
Hydras let us do “super temporary modal editing” |
Sometimes we have keybindings that share a common prefix, say C-c j
and C-c k
,
and we invoke them in an arbitrary sequence, it would be nice to invoke the
shared prefix only once thereby having:
C-c j C-c j C-c k C-c k M-3 C-c j M-5 C-c k |
≈ | C-c jjkk3j5k |
- The “hydra-zoom” example from the documentation really showcases this utility.
- After the prefix is supplied, all extensions are shown in a minibuffer.
;; Invoke all possible key extensions having a common prefix by ;; supplying the prefix only once. (use-package hydra)
From the Hydra repository is a ‘description for poets’:
Once you summon the Hydra through the prefixed binding (the body + any one head), all heads can be called in succession with only a short extension.
The Hydra is vanquished once Hercules, any binding that isn't the Hydra's head, arrives. Note that Hercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper command. This makes the Hydra very seamless, it's like a minor mode that disables itself auto-magically.
⇒ The Hydra Wiki has many example hydras for common uses cases ⇐
Below are two examples; one to simplify textual navigation and another for
window navigation.
Yet another possible hydra would be to avoid remembering word operations, such
as copying a word, upcasing it, killing a word from anywhere within it —in
contrast kill-word
kills to the end of the word—, etc. Likewise for line
operations, such as copying a line from anywhere in it. See below for another small and useful example.
When there are multiple actions, it's nice to see such a menu displayed in the middle of the frame; so we use hydra-posframe. Moreover, it can be useful to group related actions under a common heading —e.g., textual navigation may occur at the line level or word level or screen level— we obtain a nice interface by declaraing hydras using pretty-hydra-define —this saves us the trouble of formating docstrings using classic hydra.
;; TODO Fix me, breaking Github Actions test setup ;; Show hydras overlyaed in the middle of the frame ;; (use-package hydra-posframe ;; :quelpa (hydra-posframe :fetcher git :url ;; "https://github.com/Ladicle/hydra-posframe.git") ;; :hook (after-init . hydra-posframe-mode) ;; :custom (hydra-posframe-border-width 5)) ;; Neato doc strings for hydras (use-package pretty-hydra)
To actually define hydras, we make a helper function: my/defhydra —which combines defhydra and pretty-hydra-define.
Implementation
;; TODO convert my existing defhydras to my/defhydra. (defmacro my/defhydra (key title icon-name &rest body) "Make a hydra whose heads appear in a pretty pop-up window. Heads are signalled by keywords and the hydra has an icon in its title. KEY [String]: Global keybinding for the new hydra. TITLE [String]: Either a string or a plist, as specified for pretty-hydra-define. The underlying Lisp function's name is derived from the TITLE; which is intentional since hydra's are for interactive, pretty, use. One uses a plist TITLE to specify what a hydra should do *before* any options, or to specify an alternate quit key (:q by default). ICON-NAME [Symbol]: Possible FontAwesome icon-types: C-h v `all-the-icons-data/fa-icon-alist'. BODY: A list of columns and entries. Keywords indicate the title of a column; 3-lists (triples) indicate an entry key and the associated operation to perform and, optionally, a name to be shown in the pop-up. See DEFHYDRA for more details. For instance, the verbose mess: ;; Use ijkl to denote ↑←↓→ arrows. (global-set-key (kbd \"C-c w\") (pretty-hydra-define my/hydra/\\t\\tWindow\\ Adjustment ;; Omitting extra work to get an icon into the title. (:title \"\t\tWindow Adjustment\" :quit-key \"q\") (\"Both\" ((\"b\" balance-windows \"balance\") (\"s\" switch-window-then-swap-buffer \"swap\")) \"Vertical adjustment\" ((\"h\" enlarge-window \"heighten\") (\"l\" shrink-window \"lower\")) \"Horizontal adjustment\" ((\"n\" shrink-window-horizontally \"narrow\") (\"w\" enlarge-window-horizontally \"widen\" ))))) Is replaced by: ;; Use ijkl to denote ↑←↓→ arrows. (my/defhydra \"C-c w\" \"\t\tWindow Adjustment\" windows :Both (\"b\" balance-windows \"balance\") (\"s\" switch-window-then-swap-buffer \"swap\") :Vertical_adjustment (\"h\" enlarge-window \"heighten\") (\"l\" shrink-window \"lower\") :Horizontal_adjustment (\"n\" shrink-window-horizontally \"narrow\") (\"w\" enlarge-window-horizontally \"widen\"))" (let* ((name (intern (concat "my/hydra/" (if (stringp title) title (plist-get title :title))))) (icon-face `(:foreground ,(face-background 'highlight))) (iconised-title (concat (when icon-name (concat (all-the-icons-faicon (format "%s" icon-name) :face icon-face :height 1.0 :v-adjust -0.1) " ")) (propertize title 'face icon-face)))) `(global-set-key (kbd ,key) (pretty-hydra-define ,name ,(if (stringp title) (list :title iconised-title :quit-key "q") title) ,(thread-last body (-partition-by-header #'keywordp) (--map (cons (s-replace "_" " " (s-chop-prefix ":" (symbol-name (car it)))) (list (cdr it)))) (-flatten-n 1))))))
2.12. Helpful Utilities & Shortcuts
Let's save a few precious seconds,
;; change all prompts to y or n (fset 'yes-or-no-p 'y-or-n-p) ;; Make RETURN key act the same way as “y” key for “y-or-n” prompts. ;; E.g., (y-or-n-p "Happy?") accepts RETURN as “yes”. (define-key y-or-n-p-map [return] 'act) ;; Enable all ‘possibly confusing commands’ such as helpful but ;; initially-worrisome “narrow-to-region”, C-x n n. (setq-default disabled-command-function nil)
3. Staying Sane
3.1. Undo-tree: Very Local Version Control
undo-tree-visualize, C-x u, gives a visual representation of the current buffer's edit history.
;; Allow tree-semantics for undo operations. (use-package undo-tree :diminish ;; Don't show an icon in the modeline :bind ("C-x u" . undo-tree-visualize) :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME. :config ;; Always have it on (global-undo-tree-mode) ;; Each node in the undo tree should have a timestamp. (setq undo-tree-visualizer-timestamps t) ;; Show a diff window displaying changes between undo nodes. (setq undo-tree-visualizer-diff t)) ;; Execute (undo-tree-visualize) then navigate along the tree to witness ;; changes being made to your file live!
( We're just showing the <<undo-tree-setup>>
from earlier since this is a good
place for such a setup. More importantly, we are not copy-pasting the setup: It
is written only once; in a single source of truth! )
;; By default C-z is suspend-frame, i.e., minimise, which I seldom use. (global-set-key (kbd "C-z") (lambda () (interactive) (undo-tree-mode) ;; Ensure the mode is on (undo-tree-visualize)))
3.2. Automatic Backups
By default, Emacs saves backup files —those ending in ~
— in the current
directory, thereby cluttering it up. Let's place them in ~/.emacs.d/backups
, in
case we need to look for a backup; moreover, let's keep old versions since
there's disk space to go around —what am I going to do with 500gigs when nearly
all my ‘software’ is textfiles interpreted within Emacs 😼
;; New location for backups. (setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) ;; Silently delete execess backup versions (setq delete-old-versions t) ;; Only keep the last 1000 backups of a file. (setq kept-old-versions 1000) ;; Even version controlled files get to be backed up. (setq vc-make-backup-files t) ;; Use version numbers for backup files. (setq version-control t)
Why backups? Sometimes I may forget to submit a file, or edit, to my version control system, and it'd be nice to be able to see a local automatic backup. Whenever ‘I need space,’ then I simply empty the backup directory, if ever. That the backups are numbered is so sweet ^_^
Like package installations, my backups are not kept in any version control system, like git; only locally.
Finally, let's not create .#
files (crashes 'npm') nor bother confirming killing processes.
(setq confirm-kill-processes nil create-lockfiles nil)
3.2.1. What changed? —Walking through backups
Let's use an elementary diff system for backups: backup-walker
essentially makes
all our backups behave as if they were (implicitly) version controlled.
(use-package backup-walker :commands backup-walker-start)
In a buffer that corresponds to a file, invoke backup-walker-start to see a
visual diff of changes between versions; then n
and p
to move between diffs. By
default, you see the changes ‘backwards’: Red means delete these things to get
to the older version; i.e., the red ‘-’ are newer items.
There is also diff-backup for comparing a file with its backup.
3.2.2. Save ≈ Backup
Emacs only makes a backup the very first time a buffer is saved; I'd prefer Emacs makes backups everytime I save! —If I saved, that means I'm at an important checkpoint, so please check what I have so far as a backup!
;; Make Emacs backup everytime I save (defun my/force-backup-of-buffer () "Lie to Emacs, telling it the curent buffer has yet to be backed up." (setq buffer-backed-up nil)) (add-hook 'before-save-hook 'my/force-backup-of-buffer) ;; [Default settings] ;; Autosave when idle for 30sec or 300 input events performed (setq auto-save-timeout 30 auto-save-interval 300)
It is intestesting to note that the above snippet could be modified to make our own backup system, were Emacs lacked one, by having our function simply save copies of our file —on each save— where the filename is augmented with a timestamp.
3.3. magit
—Emacs' porcelain interface to git
Let's setup an Emacs ‘porcelain’ interface to git —it makes working with version control tremendously convenient.
(Personal reminder: If using 2FA [two factor authentication], then when you do
git operations, such as git push
, you must use your PAT [personal access token]
instead of your password! Also: Install refined-github: Browser extension that
simplifies the GitHub interface and adds useful features!)
;; Bottom of Emacs will show what branch you're on ;; and whether the local file is modified or not. (use-package magit :init (require 'magit-files) :bind (("C-c M-g" . magit-file-dispatch)) :custom ;; Do not ask about this variable when cloning. (magit-clone-set-remote.pushDefault t))
Why use magit
as the interface to the git version control system? In a magit
buffer nearly everything can be acted upon: Press return
, or space
, to see
details and tab
to see children items, usually.
- C-x g, magit-status, gives you a nice buffer with an overview of the Git repo that you're buffer is currently visiting.
C-c M-g, magit-file-dispatch, lets you invoke Git actions on the current file directly; e.g., following up with blame, log, diff, stage, or commit the current file.
For ease, above, we have also bound this to C-c g —reminiscent of C-x g :smile:
;; When we invoke magit-status, show green/red the altered lines, with extra ;; green/red on the subparts of a line that got alerted. (system-packages-ensure "git-delta") (use-package magit-delta :hook (magit-mode . magit-delta-mode)) ;; Don't forget to copy/paste the delta config into the global ~/.gitconfig file. ;; Copy/paste this: https://github.com/dandavison/delta#get-started
Blame, magit-blame, is super nice: The buffer gets annotations for each chunk of text, regarding who authoured it, when, and their commit title. Then q to quit the blame.
Likewise, magit-log-buffer-file is super neat!
Super Simple ‘magit’ Mini-tutorial
Below is my personal quick guide to working with magit —for a full tutorial seedired
- See the contents of a particular directory.
magit-init
- Put a project under version control.
The mini-buffer will prompt you for the top level folder version.
A
.git
folder will be created there. magit-status
,C-x g
- See status in another buffer.
Press
?
to see options, including:- g
- Refresh the status buffer.
- TAB
- See collapsed items, such as what text has been changed.
q
- Quit magit, or go to previous magit screen.
s
Stage, i.e., add, a file to version control. Add all untracked files by selecting the Untracked files title.
The staging area is akin to a pet store; commiting is taking the pet home.
k
- Kill, i.e., delete a file locally.
K
- This'
(magit-file-untrack)
which doesgit rm --cached
. i
- Add a file to the project
.gitignore
file. Nice stuff =) u
- Unstage a specfif staged change highlighed by cursor.
C-u s
stages everything –tracked or not. c
- Commit a change.
- A new buffer for the commit message appears, you write it then
commit with
C-c C-c
or otherwise cancel withC-c C-k
. These commands are mentioned to you in the minibuffer when you go to commit. - You can provide a commit to each altered chunk of text!
This is super neat, you make a series of local such commits rather
than one nebulous global commit for the file. The
magit
interface makes this far more accessible than a standard terminal approach! - You can look at the unstaged changes, select a region, using
C-SPC
as usual, and commit only that if you want! - When looking over a commit,
M-p/n
to efficiently go to previous or next altered sections. - Amend a commit by pressing
a
onHEAD
.
- A new buffer for the commit message appears, you write it then
commit with
d
- Show differences, another
d
or another option.- This is magit! Each hunk can be acted upon; e.g.,
s
orc
ork
;-)
- This is magit! Each hunk can be acted upon; e.g.,
v
- Revert a commit.
x
- Undo last commit. Tantamount to
git reset HEAD~
when cursor is on most recent commit; otherwise resets to whatever commit is under the cursor. l
- Show the log, another
l
for current branch; other options will be displayed.- Here
space
shows details in another buffer while cursour remains in current buffer and, moreover, continuing to pressspace
scrolls through the other buffer! Neato.
- Here
P
- Push.
F
- Pull.
:
- Execute a raw git command; e.g., enter
whatchanged
.
Notice that every time you press one of these commands, a ‘pop-up’ of realted git options appears! Thus not only is there no need to memorise many of them, but this approach makes discovering other commands easier.
[Disabled] Homemade ‘uncomitted changes’ Notification
Let's always notify ourselves of a file that has uncommited changes —we might have had to step away from the computer and forgotten to commit.
(require 'magit-git) (defun my/magit-check-file-and-popup () "If the file is version controlled with git and has uncommitted changes, open the magit status popup." (let ((file (buffer-file-name))) (when (and file (magit-anything-modified-p t file)) (message "This file has uncommited changes!") (when nil ;; Became annyoying after some time. (split-window-below) (other-window 1) (magit-status))))) ;; I usually have local variables, so I want the message to show ;; after the locals have been loaded. (add-hook 'find-file-hook '(lambda () (add-hook 'hack-local-variables-hook 'my/magit-check-file-and-popup)))
3.3.1. Credentials: I am who I am
First, let's setup our git credentials.
;; See here for a short & useful tutorial: ;; https://alvinalexander.com/git/git-show-change-username-email-address (when (equal "" (shell-command-to-string "git config user.email ")) (shell-command (format "git config --global user.name \"%s\"" user-full-name)) (shell-command (format "git config --global user.email \"%s\"" user-mail-address))) ;; Also need to customise email routes per organization ;; https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#customizing-email-routes-per-organization (ignore-error (unless my/personal-machine? (shell-command (format "git config --global user.email \"%s\"" work/email)))) ;; If we ever need to use Git in the terminal, it should be done with Emacs as ;; the underlying editor (shell-command "git config --global core.editor emacs")
3.3.2. Encouraging useful commit messages
Let's try our best to have a useful & consistent commit log:
(defun my/git-commit-reminder () (insert "\n\n# The commit subject line ought to finish the phrase: # “If applied, this commit will ⟪your subject line here⟫.” ") (beginning-of-buffer)) (add-hook 'git-commit-setup-hook 'my/git-commit-reminder)
Super neat stuff!
3.3.3. Maybe clone … everything?
Below are the git repos I'd like to clone —along with a function to do so quickly.
;; Clone git repo from clipboard (cl-defun maybe-clone (remote &optional local) "Clone a REMOTE repository [from clipboard] if the LOCAL directory does not exist. If called interactively, clone URL in clipboard into ~/Downloads then open in dired. Yields ‘repo-already-exists’ when no cloning transpires, otherwise yields ‘cloned-repo’. LOCAL is optional and defaults to the base name; e.g., if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y." (interactive "P") (when (interactive-p) (setq remote (substring-no-properties (current-kill 0))) (cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" remote) nil "No URL in clipboard")) (unless local (setq local (concat "~/" (if (interactive-p) "Downloads/" "") (file-name-base remote)))) (require 'magit-repos) ;; Gets us the magit-repository-directories variable. (add-to-list 'magit-repository-directories `(,local . 0)) (if (file-directory-p local) 'repo-already-exists (shell-command (concat "git clone " remote " " local)) (dired local) 'cloned-repo)) (maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d") (maybe-clone "https://github.com/alhassy/alhassy.github.io" "~/blog") (maybe-clone "https://github.com/alhassy/holy-books")
Many more repos to clone
(maybe-clone "https://github.com/alhassy/melpa") (maybe-clone "https://github.com/alhassy/org-special-block-extras") ;; (maybe-clone "https://github.com/alhassy/next-700-module-systems-proposal.git" "~/thesis-proposal") ;; (maybe-clone "https://github.com/JacquesCarette/MathScheme") ;; (maybe-clone "https://github.com/alhassy/gentle-intro-to-reflection" "~/reflection/") ;; (maybe-clone "https://github.com/alhassy/org-agda-mode") ;; (maybe-clone "https://github.com/JacquesCarette/TheoriesAndDataStructures") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/RATH/RATH-Agda" "~/RATH-Agda") ;; (maybe-clone "https://github.com/alhassy/MyUnicodeSymbols") ;; Deleted? (maybe-clone "https://github.com/alhassy/islam") (maybe-clone "https://github.com/alhassy/CheatSheet") (maybe-clone "https://github.com/alhassy/ElispCheatSheet") ;; (maybe-clone "https://github.com/alhassy/CatsCheatSheet") ;; (maybe-clone "https://github.com/alhassy/OCamlCheatSheet") ;; (maybe-clone "https://github.com/alhassy/AgdaCheatSheet") (maybe-clone "https://github.com/alhassy/RubyCheatSheet") ;; (maybe-clone "https://github.com/alhassy/PrologCheatSheet") ;; (maybe-clone "https://github.com/alhassy/FSharpCheatSheet") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/armstmp/cs3mi3.git" "~/3mi3") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/CAS781" "~/cas781") ;; cat adventures ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/carette/cs3fp3.git" "~/3fp3") ;; (maybe-clone "https://github.com/alhassy/interactive-way-to-c") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-distribution.git" "~/3ea3/assignment-distribution") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/notes.git" "~/3ea3/notes") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-development.git" "~/3ea3/assignment-development") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/kandeeps.git" "~/3ea3/sujan") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/horsmane.git" "~/3ea3/emily") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/anderj12.git" "~/3ea3/jacob") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/3EA3.git" "~/3ea3/_2018") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/2DM3/LectureNotes.git" "~/2dm3")
This maybe-clone utility has genuinely
made it easier for me to learn about new projects and codebases from Github:
I type it in with the repo's address, then C-x C-e ---eval-last-sexp—
and then I can view it in my beloved Emacs (─‿‿─)
.
Moreover, this handy tool makes it so that you can list your Git repositories with magit-list-repositories: It marks modified repos as “dirty”.
It may be useful to know that (magit-anything-modified-p t file)
can be used to
check if file
has been modified (magit-anything-modified-p), whereas
(magit-status repo)
checks the status of a repository (magit-status).
3.3.4. Gotta love that time machine
Finally, one of the main points for using version control is to have access to historic versions of a file. The following utility allows us to M-x git-timemachine on a file and use p/n/g/q to look at previous, next, goto arbitrary historic versions, or quit.
(use-package git-timemachine :defer t)
If we want to roll back to a previous version, we just write-file or C-x C-s as usual! The power of text!
vc-annotate is also very useful to go through history and work out when things went wrong.
3.3.5. Jump to a (ma)git repository with C-u C-x g
;; Jump to a (ma)git repository with C-u C-x g. ;; ;; To get a selection of repositories (that have been visited at least once), ;; call with “C-u M-x magit-status” or “C-u C-x g”; use “C-u C-u C-x g” to ;; manually enter a path to a repository. ;; ;; We use projectile's record of known projects, and keep only projects with ;; .git directory. (with-eval-after-load 'projectile (setq magit-repository-directories (thread-last (projectile-relevant-known-projects) (--filter (unless (file-remote-p it) (file-directory-p (concat it "/.git/")))) (--map (list (substring it 0 -1) 0))))) ;; Follow-up utility (defun my/update-repos () "Update (git checkout main & pull) recently visited repositories." (interactive) (cl-loop for (repo _depth) in magit-repository-directories ;; Is it “main” or “master” for trunk = (s-trim (shell-command-to-string (format "cd %s; git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'" repo))) do (message (format "🤖 %s ∷ Checking out & pulling main" repo)) (shell-command (format "cd %s; git checkout %s; git pull" repo trunk))) (message "🥳 Happy coding!"))
3.4. Pretty Magit Commit Leaders
⟨ Following Pretty Magit - Integrating commit leaders | Modern Emacs ⨾⨾ Code comes from there as well. Notable alteration: Helm compleition shows description of leaders. ⟩
Add faces to Magit to achieve icon and colored commit leaders. I also integrate Helm to prompt a leader when committing so there's no need to remember or type out completely every leader we choose.
- It's not just aesthetics. It's about visual clarity.
- Here is an alternate approach: Add icons based on words mentioned in commit titles —no leaders required.
Boring details ~ See linked article instead
(cl-defmacro pretty-magit (WORD ICON PROPS &optional (description "") NO-PROMPT?) "Replace sanitized WORD with ICON, PROPS and by default add to prompts." `(prog1 (add-to-list 'pretty-magit-alist (list (rx bow (group ,WORD (eval (if ,NO-PROMPT? "" ":")))) ,ICON ',PROPS)) (unless ,NO-PROMPT? (add-to-list 'pretty-magit-prompt (cons (concat ,WORD ": ") ,description))))) (setq pretty-magit-alist nil) (setq pretty-magit-prompt nil)
My personal choices for leaders are:
(pretty-magit "Add" ?➕ (:foreground "#375E97" :height 1.2) "✅ Create a capability e.g. feature, test, dependency.") (pretty-magit "Delete" ?❌ (:foreground "#375E97" :height 1.2) "❌ Remove a capability e.g. feature, test, dependency.") (pretty-magit "Fix" ?🔨 (:foreground "#FB6542" :height 1.2) "🐛 Fix an issue e.g. bug, typo, accident, misstatement.") (pretty-magit "Clean" ?🧹 (:foreground "#FFBB00" :height 1.2) "✂ Refactor code; reformat say by altering whitespace; refactor performance.") (pretty-magit "Document" ?📚 (:foreground "#3F681C" :height 1.2) "ℹ Refactor of documentation, e.g. help files.") (pretty-magit "Feature" ?⛲ (:foreground "slate gray" :height 1.2) "⛳ 🇮🇶🇨🇦 A milestone commit - flagpost") (pretty-magit "Generate" ?🔭 (:foreground "slate gray" :height 1.2) "Export PDF/HTML or tangle raw code from a literate program") ;; Generating artefacts (pretty-magit "master" ? (:box t :height 1.2) "" t) (pretty-magit "origin" ?🐙 (:box t :height 1.2) "" t) ;; Commit leader examples: https://news.ycombinator.com/item?id=13889155. ;; ;; Cut ~ Remove a capability e.g. feature, test, dependency. ;; Bump ~ Increase the version of something e.g. dependency. ;; Make ~ Change the build process, or tooling, or infra. ;; Start ~ Begin doing something; e.g. create a feature flag. ;; Stop ~ End doing something; e.g. remove a feature flag.
Boring details ~ See linked article instead
(defun add-magit-faces () "Add face properties and compose symbols for buffer from pretty-magit." (interactive) (with-silent-modifications (--each pretty-magit-alist (-let (((rgx icon props) it)) (save-excursion (goto-char (point-min)) (while (search-forward-regexp rgx nil t) (compose-region (match-beginning 1) (match-end 1) icon) (when props (add-face-text-property (match-beginning 1) (match-end 1) props)))))))) (advice-add 'magit-status :after 'add-magit-faces) (advice-add 'magit-refresh-buffer :after 'add-magit-faces) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (setq use-magit-commit-prompt-p nil) (defun use-magit-commit-prompt (&rest args) (setq use-magit-commit-prompt-p t)) (defun magit-commit-prompt () "Magit prompt and insert commit header with faces." (interactive) (when use-magit-commit-prompt-p (setq use-magit-commit-prompt-p nil) (thread-last (--map (format "%s %s" (car it) (cdr it)) pretty-magit-prompt) (completing-read "Insert commit leader ∷ ") ;; My “Generate:” commit type has one use case, for now; so let's insert it filled-in. (funcall (lambda (it) (if (s-starts-with? "Generate:" it) it (car (s-split " " it))))) (insert) (end-of-line)) (add-magit-faces))) (remove-hook 'git-commit-setup-hook 'with-editor-usage-message) (add-hook 'git-commit-setup-hook 'magit-commit-prompt) (advice-add 'magit-commit :after 'use-magit-commit-prompt)
3.5. Version Control with SVN —Using Magit! Disabled
Disabled: I seldom work with SVN anymore. |
Let's use git as an interface to subversion repositories so that we can continue
to use magit
as our version control interface. The utility to do so is called
git svn
—note git 𝒳
on a MacOS is the same as git-𝒳
on other systems.
(use-package magit-svn :hook (magit-mode . magit-svn-mode))
Here's an example. The following command checksout an SVN repo; afterwhich we may open a file
there and do M-x magit-status
to get the expected porcelain git interface ^_^
(async-shell-command "mkdir ~/2fa3; git svn clone --username alhassm https://websvn.mcmaster.ca/csse2fa3/2019-2020_Term2 ~/2fa3/")
In the magit buffer, we may now use the N
key which wraps the git svn
subcommands fetch, rebase, dcommit, branch, tag
. For example:
- 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.)
3.6. Highlighting TODO-s & Showing them in Magit
Sometimes it's nice to flag a chunk of text by its author, such as ‘ MA ’ for ‘M’usa ‘A’l-hassy, or ‘ HACK ’ for text that needs to be improved. Such flags stand out from other text by being coloured and bold.
;; NOTE that the highlighting works even in comments. (use-package hl-todo ;; I want todo-words highlighted in prose, not just in code fragements. :hook (org-mode . hl-todo-mode) :config ;; Adding new keywords (cl-loop for kw in '("TEST" "MA" "WK" "JC") do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))) ;; Enable it everywhere. (global-hl-todo-mode))
We've added few to the default flag keywords so that in total we have the following flags —where any sequence of at least 3 XXX are considered flags.
(JC WK MA TEST HOLD TODO NEXT THEM PROG OKAY DONT FAIL DONE NOTE KLUDGE HACK TEMP FIXME XXX+)
Lest these get buried in mountains of text, let's have them become mentioned in
a magit status buffer —which uses the keywords from hl-todo
.
;; MA: The todo keywords work in code too! (use-package magit-todos :after magit :after hl-todo ;; :hook (org-mode . magit-todos-mode) :config ;; For some reason cannot use :custom with this package. (custom-set-variables '(magit-todos-keywords (list "TODO" "FIXME" "MA" "WK" "JC"))) ;; Ignore TODOs mentioned in exported HTML files; they're duplicated from org src. (setq magit-todos-exclude-globs '("*.html")) (magit-todos-mode))
- Note that such TODO keywords are not propagated from sections that are COMMENT-ed out in org-mode.
- Ensure you exclude generated files, such as the Emacs backups directory, from
being consulted. Using
magit
, press i to mark items to be ignored. - This feature also works outside of git repos.
Open a Magit status buffer, or run magit-todos-list to show a dedicated to-do list buffer. You can then peek at items with space, or jump to them with enter.
Seeing the TODO list with each commit is an incentive to actually tackle the items there (•̀ᴗ•́)و
3.7. Silently show me when a line was modified and by whom
Quickly & automatically glimpse who, why, and when a line or code block was changed, using blamer.el. Jump back through history to gain further insights as to how and why the code evolved with C-x g l l (magit-log-head) or git-timemachine.
(unless noninteractive (use-package blamer :quelpa (blamer :fetcher github :repo "artawower/blamer.el") :custom (blamer-idle-time 0.3) (blamer-min-offset 70) (blamer-max-commit-message-length 80) ;; Show me a lot of the commit title :custom-face (blamer-face ((t :foreground "#7a88cf" :background nil :height 140 :italic t))) :config (global-blamer-mode 1)))
This is so nice!. I've enabled it once and then it just “works in the background, silently”.
When reading a line and wondering “who changed/wrote this and why”, blamer.el answers that seamlessly —almost as if a transient comment 😁
- The “why” is answered by commit messages; which may lead to improved messages: “Hey, I was just browsing through the code, and landed on this commit, but the message does not tell me why the change was introduced. Would you please write more detailed commit messages.”
- The “when” gives a useful context in time about the change. For example, when looking for a regression/recent-bug, we can immediately see “Did this line even change in the past week? No? That must not be it then” and we move on.
If we want to see commit messages alongside all code, then we can invoke magit-blame-addition or vc-annotate. If we want to “walk-along changes” then we use git-timemachine.
3.8. delete-by-moving-to-trash t
;; Move to OS’ trash can when deleting stuff ;; instead of deleting things outright! (setq delete-by-moving-to-trash t trash-directory "~/.Trash/")
3.9. Jumping to extreme semantic units
Sometimes it's unreasonable for M-<
to take us to the actual start of a buffer;
instead it'd be preferable to go to the first “semantic unit” in the buffer. For
example, when directory editing with dired
we should jump to the first file,
with version control with magit
we should jump to the first section, when
composing mail we should jump to the first body line, and in the agenda we
should jump to the first entry.
;; M-< and M-> jump to first and final semantic units. ;; If pressed twice, they go to physical first and last positions. (use-package beginend :diminish 'beginend-global-mode :config (beginend-global-mode) (cl-loop for (_ . m) in beginend-modes do (diminish m)))
3.10. Get CheatSheets and view them easily
(defvar my/cheatsheet/cached-topics nil) (cl-defun my/cheatsheet (&optional topic) "Clone Al-hassy's ⟨TOPIC⟩CheatSheet repository when called from Lisp; visit the pretty HTML page when called interactively. - Example usage: (my/cheatsheet \"Vue\") - Example usage: M-x my/cheatsheet RET Vue RET." (interactive) (if (not topic) (browse-url (format "https://alhassy.github.io/%sCheatSheet" (completing-read "Topic: " my/cheatsheet/cached-topics))) (push topic my/cheatsheet/cached-topics) (maybe-clone (format "https://github.com/alhassy/%sCheatSheet" topic))))
Let's actually get some repos locally, and use: M-x my/cheatsheet
to view the pretty HTML (or PDF) sheets.
(mapcar #'my/cheatsheet '("ELisp" "GojuRyu" "Rust")) ; Python Prolog Vue Agda JavaScript ; Clojure Ruby Oz Coq Cats Haskell FSharp OCaml
4. Literate Programming
Org-mode lets us run chunks of code anywhere, then feed their outputs to other chunks of code in possibly different programming languages: Org is a meta-(programming language).
Importantly, this means we can write text and whenever we need the result of some computation, we can place it there and then and only request its result appear in PDF/HTML export. The result is a single document.
( There is the org-modern package, which provides a modern look-and-feel: It makes Org look less like a markup and more like a word editor. Nice stuff. )
4.1. High Speed Literate Programming
4.1.1. Manipulating Sections
(setq org-use-speed-commands t)
This enables the Org Speed Keys so that when the cursor is at the beginning of a headline, we can perform fast manipulation & navigation using the standard Emacs movement controls, such as:
- # toggle
COMMENT
-ing for an org-header. s toggles “narrowing” to a subtree; i.e., hide the rest of the document.
If you narrow to a subtree then any export, C-c C-e, will joyously only consider the narrowed detail.
- u for going to upwards to parent heading
- i insert a new same-level heading below current heading.
- c for cycling structure below current heading, or
C
for cycling global structure. - w refile current heading; options list pops-up to select which heading to move
it to. Neato!
g to go to another heading, without refiling anything.
;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful. ;; When I'm refiling my TODOS, then give me all the freedom. (setq org-refile-targets '((nil :maxlevel . 1) (org-agenda-files :maxlevel . 9))) ;; Maybe I want to refile into a new heading; confirm with me. (setq org-refile-allow-creating-parent-nodes 'confirm) ;; Use full outline paths for refile targets ;; When refiling, using Helm, show me the hierarchy paths (setq org-outline-path-complete-in-steps nil) (setq org-refile-use-outline-path 'file-path)
- n/p for next/previous visible heading.
- f/b for jumping forward/backward to the next/previous same-level heading.
- D/U move a heading down/up.
- L/R recursively promote (move leftwards) or demote (more rightwards) a heading.
- I/O clock In/Out to the task defined by the current heading.
- Keep track of your work times!
- v view agenda.
- t/,/:/e to add a TODO state, priority level, tag, or effort estimate
- 1/2/3 to mark a heading with priority, highest to lowest.
- ^ sort children of current subtree; brings up a list of sorting options.
- k/@/a to kill or mark or archive the current subtree
- o to open a link mentioned in the subtree then go to the link; a pop-up of links appears.
We can add our own speed keys by altering the org-speed-commands association list variable; e.g.,
;; TODO FIXME Crashes upon startup. (when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property))) ;; Use ‘:’ and ‘e’ to set tags and effort, respectively.
⇒ Moreover, ? to see a complete list of keys available. ⇐ |
4.1.3. Modifying ⟨return⟩
- C-⟨return⟩ , C-S-⟨return⟩ make a new heading where the latter marks it as a
TODO
. - By default M-⟨return⟩ makes it easy to work with existing list items, headings, tables, etc by creating a new item, heading, etc.
Usually we want a newline then we indent, let's make that the default.
(add-hook 'org-mode-hook '(lambda () (local-set-key (kbd "<return>") 'org-return-indent)) (local-set-key (kbd "C-M-<return>") 'electric-indent-just-newline))
Notice that I've also added another kind of return, for when I want to break-out of the indentation approach and start working at the beginning of the line.
In summary:
key | method | behaviour |
---|---|---|
⟨return⟩ | org-return-indent | Newline with indentation |
M-⟨return⟩ | org-meta-return | Newline with new org item |
C-M-⟨return⟩ | electric-indent-just-newline | Newline, cursor at start |
C-⟨return⟩ | org-insert-heading-respect-content | New heading after current content |
C-S-⟨return⟩ | org-insert-todo-heading-respect-content | Ditto, but with a TODO marker |
4.2. Executing code from src
blocks
For example, to execute a shell command in Emacs, write a src
with a shell
command, then C-c c-c
to see the results. Emacs will generally query you to
ensure you're confident about executing the (possibly dangerous) code block;
let's stop that:
;; Seamless use of babel: No confirmation upon execution. ;; Downside: Could accidentally evaluate harmful code. (setq org-confirm-babel-evaluate nil) ;; Never evaluate code blocks upon export and replace results when evaluation does occur. ;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’. (setq org-babel-default-header-args '((:results . "replace") (:session . "none") (:exports . "both") (:cache . "no") (:noweb . "no") (:hlines . "no") (:tangle . "no") (:eval . "never-export")))
Some initial languages we want org-babel to support:
(defvar my/programming-languages '(emacs-lisp shell python haskell rust ruby ocaml dot latex org js css sqlite C) ;; Captial “C” gives access to C, C++, D "List of languages I have used in Org-mode, for literate programming.") ;; Load all the languagues (cl-loop for lang in my/programming-languages do (require (intern (format "ob-%s" lang)))) ;; (org-babel-do-load-languages 'org-babel-load-languages (--map (cons it t) my/programming-languages)) ;; Preserve my indentation for source code during export. (setq org-src-preserve-indentation t) ;; The export process hangs Emacs, let's avoid this. ;; MA: For one reason or another, this crashes more than I'd like. ;; (setq org-export-in-background t)
More languages can be added using add-to-list.
4.3. Executing all #+name: startup-code
for local configurations
Sometimes my Org-files contain configurations that are local to the file,
so I name all such src
blocks #+name: startup-code
and place # -*- eval: (my/execute-startup-blocks) -*-
at the top of the file so that such
blocks are evaluated when the file opens up.
- The
-*- ... -*-
notation is for making local configurations. - Use
M-x add-file-local-variable-prop-line
to have them inserted interactively.
(defun my/execute-startup-blocks () "Execute all startup blocks, those named ‘startup-code’. I could not use ORG-BABEL-GOTO-NAMED-SRC-BLOCK since it only goes to the first source block with the given name, whereas I'd like to visit all blocks with such a name." (interactive) (save-excursion (goto-char 0) (while (ignore-errors (re-search-forward "^\\#\\+name: startup-code")) (org-babel-execute-src-block))))
The following setup enables this feature in a safe fashion —e.g., we do not want to avoid evaluating a random person's potentially dangerous code when we only want to look at it.
;; Please ask me on a file by file basis whether its local variables are ‘safe’ ;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried ;; again for the same file. (setq enable-local-variables t)
I have been using a combination of (org-babel-goto-named-src-block ⋯)
in
multi-line local-variable declarations ---M-x add-file-local-variable-prop
—
for a while in many files using a dedicated * footer :noexport:
section, but
this new approach frees from having such sections and instead to having a single
line at the top of the file. Moreover, being at the top of the file, such a line
is a nice ‘in your face’ reminder that there is local configuration that
should have been loaded.
4.4. Quickly pop-up a terminal, run a command, close it —and zsh
Pop up a terminal, do some work, then close it using the same command.
Shell-pop uses only one key action to work: If the buffer exists, and we're in
it, then hide it; else jump to it; otherwise create it if it doesn't exit. Use
universal arguments, e.g., C-u 5 C-t
, to have multiple shells and the same
universal arguments to pop those shells up, but C-t
to pop them away.
(use-package shell-pop :custom ;; This binding toggles popping up a shell, or moving cursour to the shell pop-up. (shell-pop-universal-key "C-t") ;; Percentage for shell-buffer window size. (shell-pop-window-size 30) ;; Position of the popped buffer: top, bottom, left, right, full. (shell-pop-window-position "bottom") ;; Please use an awesome shell. (shell-pop-term-shell "/bin/zsh"))
Oh My Zsh
Now that we have access to quick pop-up for a shell, let's get a pretty and practical shell: zsh along with the Oh My Zsh community configurations give us:
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, C-t tldr 𝒳 ⟨return⟩. )
(system-packages-ensure "tldr")
4.5. Snippets —Template Expansion
It is common that there is a sequence of text that we tend to repeat often, possibly with a name or some other parameter altered. Such a ‘snippet’ could be written once then provided by a simple Lisp insert command with the parameters being queried. Luckily, others have written such pleasant utilities.
Besides snippets, there are words that we may want to repeat often but it can be tedious to write them out in full. As such, we employ word completion; which we also use to expand our snippets.
4.5.1. Word Completion
Let's enable “complete anything” mode —it ought to start in half a second and only need two characters to get going, which means word suggestions are provided and so I need only type partial words then tab to get the full word!
(use-package company :diminish :config (global-company-mode 1) (setq ;; Only 2 letters required for completion to activate. company-minimum-prefix-length 2 ;; Search other buffers for compleition candidates company-dabbrev-other-buffers t company-dabbrev-code-other-buffers t ;; Show candidates according to importance, then case, then in-buffer frequency company-transformers '(company-sort-by-backend-importance company-sort-prefer-same-case-prefix company-sort-by-occurrence) ;; Flushright any annotations for a compleition; ;; e.g., the description of what a snippet template word expands into. company-tooltip-align-annotations t ;; Allow (lengthy) numbers to be eligible for completion. company-complete-number t ;; M-⟪num⟫ to select an option according to its number. company-show-numbers t ;; Show 10 items in a tooltip; scrollbar otherwise or C-s ^_^ company-tooltip-limit 10 ;; Edge of the completion list cycles around. company-selection-wrap-around t ;; Do not downcase completions by default. company-dabbrev-downcase nil ;; Even if I write something with the ‘wrong’ case, ;; provide the ‘correct’ casing. company-dabbrev-ignore-case nil ;; Immediately activate completion. company-idle-delay 0) ;; Use C-/ to manually start company mode at point. C-/ is used by undo-tree. ;; Override all minor modes that use C-/; bind-key* is discussed below. (bind-key* "C-/" #'company-manual-begin) ;; Bindings when the company list is active. :bind (:map company-active-map ("C-d" . company-show-doc-buffer) ;; In new temp buffer ("<tab>" . company-complete-selection) ;; Use C-n,p for navigation in addition to M-n,p ("C-n" . (lambda () (interactive) (company-complete-common-or-cycle 1))) ("C-p" . (lambda () (interactive) (company-complete-common-or-cycle -1))))) ;; It's so fast that we don't need a key-binding to start it!
Note that M-/
goes through a sequence of completions —and C-/
manually begins
company mode at point. Besides the arrow keys, we can also use M-
with n, p
to
navigate the options or use C-s
to search the list of suggestions.
- Company backends are available as separate packages.
- Note that by default company mode does not support completion for phrases containing hyphens —this can be altered, if desired.
Besides boring word completion, let's add support for emojis.
(use-package company-emoji :config (add-to-list 'company-backends 'company-emoji))
For example: 🥞 💻 🐵 ✉️😉 🐬 🌵.
➡️On a new line, write :
then any letter to have a tool-tip appear.
All emoji names are lowercase. ◀
- On MacOS,
C-⌘-SPC
brings up an emoji picker, where one drags desired emojis to textual areas. - Here is a list of emoji —all supported by Github.
The libraries emojify
and emojify-logos
provides cool items like :haskell:
:emacs: :org: :ruby: :python:
. Unfortunately they do not easily export to html
with org-mode, so I'm not using them.
4.5.2. Intro to Snippets
A snippet, template, mechanism is a tool that when you press some keystrokes inserts some text, possibly with some fields (‘blanks’) to fill in. Possibly interesting read:
- Snippet Expansion With Yasnippet: Save Yourself Keystrokes and Headaches —a nice before introduction to Yasnippet (“Yet another snippet”)
- Tweaking Emacs: Snippets —a brief article on using snippets for uniformity across languages and to mitigate verbosity of weak languages (i.e., those without macros).
Yasnippet is a pleasant utility for template expansion with the alluring
feature to allow arbitrary Lisp code to be executed during expansion.
The declaration of templates is verbose, requiring a particular file
hierarchy, as such I utilise Yankpad which allows me to employ
an Org-mode approach: Each template corresponds to an org heading of
the form Key:Words:For:Expansion:Here: name of snippet here
and the
template body is then the body of the org heading.
Any of Key, Words, For, Expansion, Here
will rewrite into the body
of the org tree. This is much more terse, and I even don't bother
with that; instead preferring to tangle my templates using yankpad
as a mere interface. It is important to note that Yankpad also provides
features that are not in Yassnippet, such as allowing arbitrary language
code to be executed —one simply uses an org-src block!
Here is a nice self-contained tutorial. |
There can only be one major completion backend for any mode, but
other backends can serve as secondary ones. Here's a function to
make company-yankpad
a secondary of all existing backends.
;; Add yasnippet support for all company backends ;; (cl-defun my/company-backend-with-yankpad (backend) "There can only be one main completition backend, so let's enable yasnippet/yankpad as a secondary for all completion backends. Src: https://emacs.stackexchange.com/a/10520/10352" (if (and (listp backend) (member 'company-yankpad backend)) backend (append (if (consp backend) backend (list backend)) '(:with company-yankpad))))
;; Yet another snippet extension program (use-package yasnippet :diminish yas-minor-mode :config (yas-global-mode 1) ;; Always have this on for when using yasnippet syntax within yankpad ;; respect the spacing in my snippet declarations (setq yas-indent-line 'fixed)) ;; Alternative, Org-based extension program (use-package yankpad :diminish :config ;; Location of templates (setq yankpad-file "~/.emacs.d/yankpad.org") ;; Ignore major mode, always use defaults. ;; Yankpad will freeze if no org heading has the name of the given category. (setq yankpad-category "Default") ;; Load the snippet templates ---useful after yankpad is altered (yankpad-reload) ;; Set company-backend as a secondary completion backend to all existing backends. (setq company-backends (mapcar #'my/company-backend-with-yankpad company-backends)))
With these settings, along with the company
backend, I may type a keyword then
TAB it into expansion.
Yankpad requires we have an org file that contains our templates, so we tangle
such a file ~/.emacs.d/yankpad.org
, and have all of our templates be globally
accessible. Here is the start of my file:
#+Description: This is file is generated from my init.org; do not edit. * Default :global:
Fully discussed example: Using the clipboard for Org-links
Here's an example of a common template I perform by hand —no more! I have the
expected habit of copying (to clipboard) a URL from someplace then forming a
link to it by writing [[URL] [description]]
, since the URL & syntax are already
known, let's expand those and place the cursour at the only unknown —the
description.
** my_org_insert_link: cleverly insert a link copied to clipboard [[${1:`(clipboard-yank)`}][$2]] $0
What's going on here? ( The above, verbatim: [[${1:`(clipboard-yank)`}][$2]] $0
. )
- This template is expanded with the keyword
my-org-insert-link
, then TAB. The cursour lands at position
$1
, which has default text being the result of evaluating(clipboard-yank)
.We may evaluate Lisp code anywhere by enclosing it in backticks.
- If we're satisfied with the current field, we simply tab to the next field. Otherwise, we simply write text —which overwrites the default text.
- After enough tabbing we complete the template and the cursour lands
at position
$0
.
⟪ Having default or mirrored text for $2
would not allow me to see the URL
field, lest I wish to change it or at least confirm it's what I want.
Hence, the $2
field has no default. ⟫
Let's overwrite the usual way to insert such links, via C-c C-l
.
(cl-defun org-insert-link () "Makes an org link by inserting the URL copied to clipboard and prompting for the link description only. Type over the shown link to change it, or tab to move to the description field. This overrides Org-mode's built-in ‘org-insert-link’ utility; whence C-c C-l uses the snippet." (interactive) (insert "my_org_insert_link") (yankpad-expand))
Warning! Snippet names cannot have hypens in them —in this setup at least.
The Yasnippet manual is an accessible read, as is the Yankpad manual, and
showcases many other utilities; such as having certain snippets being
enabled only in particular modes or on demand. Of note is that field $n
can be
accessed in code with the invocation (yas-field-value n)
.
Incidentally, I used this snippet setup to demo the idea of repetitious code in grouping constructs within dependently-typed languages, which was accepted and led to my doctoral research on a ‘do it yourself module system’.
The rest of this section is other templates, not much for now, concluding with actually loading this snippet mechanism globally.
The remaining subsections discuss contents of my yankpad file.
4.5.3. Org-mode Templates —A reason I “generate” templates ;)
This produces a pop-up list of org-mode block types, if src
is selected, then a
list of my commonly used languages pops-up. Alternatively, ignore the pop-up
menu and write any block or language name.
** begin: produce an org-mode block #+begin_${1:environment$(let* ((block '("src" "example" "quote" "verse" "center" "latex" "html" "ascii")) (langs '("c" "emacs-lisp" "lisp" "latex" "python" "sh" "haskell" "plantuml" "prolog")) (type (yas-choose-value block))) (concat type (when (equal type "src") (concat " " (yas-choose-value langs)))))} $0 #+end_${1:$(car (split-string yas-text))}
In this case, yas-text
is equivalent to (yas-field-value 1)
;
it generally refers to the value of the field being mirrored with ${n: ⋯yas-text⋯}
.
However, going through pop-ups takes precious time —besides being slightly annyoing. Let's introduce a template for my most utilised kind of language blocks.
** s_org: src block for org #+begin_src org $0 #+end_src
However, doing this for each language I want is a waste of time and textual
space. Why? The purpose of templates is to reduce repetition, yet the above
block would be repeated with only 3 parts ‘unknown’: The expansion keyword, the
description, and the org-mode source block name. Whence, the template text is
generated by the following basic loop —whose source block is named
my-org-lang-templates
.
;; We make an org BLOCK snippet template for each LANG the user has declared. ;; (cl-loop for (shortcut block takes-language-argument? default-text) in '(("s_" "src" t) ("is_" "inline source" t) ;; Treated specially below ("e_" "example" t) ("q_" "quote") ("v_" "verse") ("c_" "center") ("ex_" "export") ;; only HTML and LATEX ;; https://alhassy.github.io/org-special-block-extras/#Summary ("p_" "parallel" nil "\n$0\n#+columnbreak:\n") ("d_" "details" nil "${1:title}\n$0") ("ed_" "edcomm" nil "${1:editor}\n$0") ("doc_" "documentation" nil "${1: mandatory entry name}\n$0") ("def_" "latex-definitions")) for languages = (if takes-language-argument? (-cons* "org" "agda2" "any" ;; Extra ‘languages’ ;; Also include whatever languages we've loaded for literate programming. (--map (symbol-name (car it)) org-babel-load-languages)) '("")) ;; The “empty language” concat (cl-loop for lang in languages for key = (concat shortcut (if (s-blank? lang) block lang)) for description = (if (s-blank? lang) block (concat block " for " lang)) concat (if (equal "is_" shortcut) (concat "\n** " key ": " description "\nsrc_" lang "[:exports code]{$1} $0") (concat "\n** " key ": " description "\n#+begin_" block " " lang (or default-text "\n$0") "\n#+end_" block "\n"))))
The resulting text of this block, generated below, is tangled to our yankpad by
utilising a noweb source block invocation. An example of the resulting text is
the above s_org
block. The result is (last I checked) 83 template expansions
—that would have been a bit much to write by hand.
#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes <<my-org-lang-templates()>> #+end_src
nil
Now s_
, due to company mode, brings up a list of languages that I can then
scroll down through, then “enter” upon to expand. Moreover, the prefix s_
means
that the key is mostly irrelevant, since I needn't remember it because
company-mode immediately lists possible completions along with the descriptions
for the snippets. Likewise for examples with e_
or quotes with q_
. Super neat
stuff :-)
Ain't this reminiscent of meta-programming ;-)
Using noweb
invocations, any time the tangling is performed, the yankpad
is kept up to date —no personal intervention from myself.
With the advent of org-special-block-extras, I've made increased usage of links
–such as green:hello
which yields hello and [[kbd:][green]]
which yields
.
** ll_make_a_link: insert a link template ${1:`(let* ((τ (read-string "Link type: ")) (δ (read-string "Link Description: ")) (⊤ (if (s-contains? ":" τ) τ (s-concat τ ":")))) (format "[[%s][%s]]" ⊤ δ))`} $0
4.5.4. Operating System Keyboard Symbols
Write os_
then see a bunch of completions ;-)
(loop for (name expansion) in '((os-command ⌘) (os_option ⌥) (os_alt ⌥) (os_control ⌃) (os_shift ⇧) (os_backspace ⌫) (os_delete ⌫) (os_delete_forward ⌦) (os_enter ⏎) (os_return ⏎) (os_escape ⎋) (os_tab_right ⇥) (os_tab_left ⇤) (os_caps_lock ⇪) (os_eject ⏏)) concat (format "** %s: %s Operating System Keyboard Symbol\n%s\n" name expansion expansion))
nil
4.5.5. Work Templates
** ll_console_log: Log some JS variables console.log("%c ******* LOOK HERE *******", "color: green; font-weight: bold;"); console.log({ ${1:List the variables here whose values you want to log} }); $0 ** uuidgen: Insert the result of “uuidgen” and copy it to the clipboard ${1:`(-let [it (shell-command-to-string "uuidgen | tr '[:upper:]' '[:lower:]' | pbcopy; pbpaste")] (message "Copied to clipboard, uuid: %s" it) it)`}
4.5.6. Elisp Templates
The following snippets were rather useful as I began learning Lisp to construct
my editor of choice —I love Emacs so much. Admittedly, I still need the first
one below and usually beat around the bush by using (cl-loop for ⋯ do ⋯)
, (cl-loop), which is
‘noisier’ but easier to remember and to read for non-Lispers.
** loop: Elisp's for each loop (dolist (${1:var} ${2:list-form}) ${3:body}) ** defun: Lisp functions (cl-defun ${1:fun-name} (${2:arguments}) "${3:documentation}" $0) ** cond: Elisp conditionals (cond (${1:scenario₁} ${2:response₁}) (${3:scenario₂} ${4:response₂}))
4.5.7. Equational Templates
To show ℒ = ℛ
, one starts at the complicated side, say ℒ, then, with the aim of
simplification, tries to end at the simpler side, 𝓡. Along the way, one
justifies each step of the calculation. This approach is popular in the proof
assistant Agda; Examples. Read more about informal calculational proofs.
** fun: Function declaration with type signature ${1:fun-name} : ${2:arguments} $1 ${3:args} = ?$0 ** eqn_begin: Start a ≡-Reasoning block in Agda begin ${1:complicated-side} $0≡⟨ ${3:reason-for-the-equality} ⟩ ${2:simpler-side} ∎ ** eqn_step: Insert a step in a ≡-Reasoning block in Agda ≡⟨ ${2:reason-for-the-equality} ⟩ ${1:new-expression} $0
One expands eqn_begin
, tabs to fill in the three main locations, then
immediately types eqn_step
to produce a new step in a calculational proof.
4.5.8. Fixed replies
Here are some replies that I sometimes need to produce; e.g., to people who insist their way is the right way.
** reply_opinionated_pantomath: What to say to, e.g., an arrogant academic Your certainty inspires me to continuing exploring, and I may arrive at your point of view, but I'm going to need more evidence first. ** reply_em_dashes: Why use em dashes for parenthetical remarks? According to the “Canadian Style Guide” (CSG): The em is an expansive, attention-seeking dash. It supplies much stronger emphasis than the comma, colon or semicolon it often replaces. Positioned around interrupting elements, em dashes have the opposite effect of parentheses—em dashes emphasize; parentheses minimize. From “A Logical Approach to Discrete Math” (LADM), page ix: We place a space on one side of an em dash ---here are examples--- in order to help the reader determine whether the em dash begins or ends a parenthetical remark. In effect, we are creating two symbols from one. In longer sentences---and we do write long sentences from time to time---the lack of space can make it difficult to see the sentence structure---especially if the em dash is used too often in one sentence. Parenthetical remarks delimited by parentheses (like this one) have a space on one side of each parenthesis, so why not parenthetical remarks delimited by em dashes? Interestingly, according to the CSG, there should be no space before or after an em dash. As such, it appears that the spacing is mostly stylistic; e.g., some people surround em-s with spaces on both sides. In particular, when em-s are unmatched, I make no use of additional space ---indeed this form of one-sided parentheses without a space is how LADM is written, as can be seen at the top of page 3.
4.5.9. Emojis
;; ;; https://emojipedia.org/people/ (cl-loop for (emoji name description) in '((😀 "Grinning Face" "Often conveys general pleasure and good cheer or humor.") (😃 "Grinning Face with Big Eyes" "Often conveys general happiness and good-natured amusement. Similar to 😀 Grinning Face but with taller, more excited eyes.") (😄 "Grinning Face with Smiling Eyes" "Often conveys general happiness and good-natured amusement. Similar to 😀 Grinning Face and 😃 Grinning Face With Big Eyes, but with warmer, less excited eyes.") (😁 "Beaming Face with Smiling Eyes" "Often expresses a radiant, gratified happiness. Tone varies, including warm, silly, amused, or proud.") (😆 "Grinning Squinting Face" "Often conveys excitement or hearty laughter. Similar to 😀 Grinning Face but with eyes that might say ‘Squee!’ or ‘Awesome!’ An emoji form of the >< or xD emoticons.") (😅 "Grinning Face with Sweat" "Intended to depict nerves or discomfort but commonly used to express a close call, as if saying ‘Whew!’ and wiping sweat from the forehead. ") (🤣 "Rolling on the Floor Laughing" "Often conveys hysterical laughter more intense than 😂 Face With Tears of Joy.") (😂 "Face with Tears of Joy") (🙂 "Slightly Smiling Face") (🙃 "Upside-Down Face") (😉 "Winking Face") (😊 "Smiling Face with Smiling Eyes") (😇 "Smiling Face with Halo") (🥰 "Smiling Face with Hearts") (😍 "Smiling Face with Heart-Eyes") (🤩 "Star-Struck") (😘 "Face Blowing a Kiss") (😗 "Kissing Face") (☺️ "Smiling Face") (😚 "Kissing Face with Closed Eyes") (😙 "Kissing Face with Smiling Eyes") (🥲 "Smiling Face with Tear") (😋 "Face Savoring Food") (😛 "Face with Tongue") (😜 "Winking Face with Tongue") (🤪 "Zany Face") (😝 "Squinting Face with Tongue") (🤑 "Money-Mouth Face") (🤗 "Hugging Face") (🤭 "Face with Hand Over Mouth") (🤫 "Shushing Face") (🤔 "Thinking Face") (🤐 "Zipper-Mouth Face") (🤨 "Face with Raised Eyebrow") (😐 "Neutral Face") (😑 "Expressionless Face") (😶 "Face Without Mouth") (😏 "Smirking Face") (😒 "Unamused Face") (🙄 "Face with Rolling Eyes") (😬 "Grimacing Face") (🤥 "Lying Face") (😌 "Relieved Face") (😔 "Pensive Face") (😪 "Sleepy Face") (🤤 "Drooling Face") (😴 "Sleeping Face") (😷 "Face with Medical Mask") (🤒 "Face with Thermometer") (🤕 "Face with Head-Bandage") (🤢 "Nauseated Face") (🤮 "Face Vomiting") (🤧 "Sneezing Face") (🥵 "Hot Face") (🥶 "Cold Face") (🥴 "Woozy Face") (😵 "Dizzy Face") (🤯 "Exploding Head") (🤠 "Cowboy Hat Face") (🥳 "Partying Face") (🥸 "Disguised Face") (😎 "Smiling Face with Sunglasses") (🤓 "Nerd Face") (🧐 "Face with Monocle") (😕 "Confused Face") (😟 "Worried Face") (🙁 "Slightly Frowning Face") (☹️ "Frowning Face") (😮 "Face with Open Mouth") (😯 "Hushed Face") (😲 "Astonished Face") (😳 "Flushed Face") (🥺 "Pleading Face") (😦 "Frowning Face with Open Mouth") (😧 "Anguished Face") (😨 "Fearful Face") (😰 "Anxious Face with Sweat") (😥 "Sad but Relieved Face") (😢 "Crying Face") (😭 "Loudly Crying Face") (😱 "Face Screaming in Fear") (😖 "Confounded Face") (😣 "Persevering Face") (😞 "Disappointed Face") (😓 "Downcast Face with Sweat") (😩 "Weary Face") (😫 "Tired Face") (🥱 "Yawning Face") (😤 "Face with Steam From Nose") (😡 "Pouting Face") (😠 "Angry Face") (🤬 "Face with Symbols on Mouth") ) for nom = (s-replace " " "_" name) for desc = (s-collapse-whitespace (or description "")) concat (concat ;; f_… ⇒ get emoji from company menu showing only name & emoji (format "\n** f_%s: %s %s \n%s" nom emoji "" emoji) ;; fd_… ⇒ get emoji from company menu showing name, emoji, & ‘d’escription (format "\n** fd_%s: %s %s \n%s" nom emoji desc emoji)))
;; Get all unicode emojis to appear within Emacs ;; See also: https://emacs.stackexchange.com/questions/5689/force-a-single-font-for-all-unicode-glyphs?rq=1 (unless noninteractive (set-fontset-font t nil "Apple Color Emoji"))
4.5.10. my_⋯
Templates to obtain User Information
Let's add templates for links to common user information ^_^
** my_name: User's name `user-full-name` ** my_email: User's email address `user-mail-address` ** my_github: User's Github repoistory link https://github.com/alhassy/ ** my_emacsdrepo: User's version controlled Emacs init file https://github.com/alhassy/emacs.d ** my_blog: User's blog website https://alhassy.github.io/ ** my_webpage: User's organisation website http://www.cas.mcmaster.ca/~alhassm/ ** my_twitter: User's Twitter profile https://twitter.com/musa314 ** my_masters_thesis A Mechanisation of Internal Galois Connections In Order Theory Formalised Without Meets https://macsphere.mcmaster.ca/bitstream/11375/17276/2/thesis.pdf
It may be useful to also have Org-link variants of these …
4.5.11. Templates from other places in my init
In this setup, I have some templates appear elsewhere, tagged with :noweb-ref
templates-from-other-places-in-my-init
. They are presented in natural
positions, but can only occur to the machine after template expansion is setup.
Using org-mode, we are able to present code in any order and tangle it to
the order the compilers need it to be!
Let's activate all such templates, now after template expansion has been setup.
#+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none <<templates-from-other-places-in-my-init>> #+end_src
You can press C-c C-v C-v, org-babel-expand-src-block, to see what this block expands into…
Expansion
** journal_guided: Introspection & Growth I'm writing from ${1:location}. Gut answer, today I feel ${2:scale}/10. ⇒ ${3:Few words or paragraphs to explain what's on your mind.} ${4: All things which cause us to groan or recoil are part of the tax of life. These things you should never hope or seek to escape. Life is a battle, and to live is to fight. ⟨ Press TAB once you've read this mantra. ⟩ $(when yas-moving-away-p "") } `(progn (eww "https://www.dailyinspirationalquotes.in/") (sit-for 2) (when nil let eww load) (read-only-mode -1) (goto-line 52) (kill-line) (kill-buffer) (yank))` ${7: Self Beliefs: + I am working on a healthier lifestyle, including a low-carb diet. - I’m also investing in a healthy, long-lasting relationship. ➩ These are what I want and are important to me. ⇦ + I will not use any substances to avoid real issues in my life. I must own them. + Everything I’m searching for is already inside of me. + Progress is more important than perfection. ⟨ Press TAB once you've read these beliefs. ⟩ $(when yas-moving-away-p "") } *Three things I'm grateful for:* 1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} 2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} 3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} *Three things that would make today great:* 1. ${11:???} 2. ${12:???} 3. ${13:???} *What one thing is top of mind today?* ${14:???} *What’s one opportunity I want to go after?* ${15:???} *What’s one thing I’m really proud of OR I’m amazed and in awe of?* ${16:???} $0
Note: Since I've insisted that Org blocks are space sensative, any whitespace
before the <<⋯>>
will propogate to the resulting extracted code.
Warning!
This section had
:PROPERTIES: :CUSTOM_ID: Templates-from-other-places-in-my-init :END:
Which, as of Org 9.4, led to the entire section being tangled: This is what the
above incantation requested, but I thought it only worked on src blocks, having
the specified :noweb-ref
, not on :CUSTOM_ID:
incidentally having the same
name.
4.6. Prettify inline source code
;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”. ;; (font-lock-add-keywords 'org-mode '(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)" (1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾")) (2 '(face (:foreground "blue"))) (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) ))) ;; ;; Let's do this for all my languages: ;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”. (cl-loop for lang in my/programming-languages do (font-lock-add-keywords 'org-mode `(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang) (1 '(face (:inherit (bold) :foreground "gray65") display "﴾")) (2 '(face (:foreground "blue"))) (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) )))) ;; (defun my/toggle-line-fontification () "Toggle the fontification of the current line" (interactive) (defvar my/toggle-fontify/current-line -1) (defvar my/toggle-fontify/on? nil) (add-to-list 'font-lock-extra-managed-props 'display) (let ((start (line-beginning-position)) (end (line-end-position))) (cond ;; Are we toggling the current line? ((= (line-number-at-pos) my/toggle-fontify/current-line) (if my/toggle-fontify/on? (font-lock-fontify-region start end) (font-lock-unfontify-region start end)) (setq my/toggle-fontify/on? (not my/toggle-fontify/on?))) ;; Nope, we've moved on to another line. (:otherwise (setq my/toggle-fontify/current-line (line-number-at-pos) my/toggle-fontify/on? :yes_please_fontify) (font-lock-unfontify-region start end))))) ;; TODO FIXME; maybe ignore: Wasted too much time here already. ;; (add-hook 'post-command-hook #'my/toggle-line-fontification nil t) ;; (font-lock-add-keywords nil '((my/toggle-line-fontification)) t)
5. Life within Org-mode
It's hard to estimate how long a task takes if you don't keep track of time spent by ‘clocking-in and clocking-out’ of tasks. We can ‘capture’ todos right in the middle of a task without context-switching; e.g., no opening a todos file! After some reflection on the relative importance of the tasks, we can schedule them into our ‘agenda’.
Let's do this!
5.1. Using Org-Mode as a Day Planner
⟪ This section is based on a dated, yet delightful, tutorial of the same title by John Wiegley. ⟫
We want a day-planner with the following use:
- “Mindlessly” & rapidly create new tasks.
- Schedule and archive tasks at the end, or start, of the work day.
- Glance at a week's tasks, shuffle if need be.
- Prioritise the day's tasks. Aim for ≤15 tasks.
- Progress towards completion of
A
tasks by documenting work completed. - Repeat! During the day, if anything comes up, capture it and intentionally forget about it.
5.1.1. Capturing ideas & notes without interrupting the current workflow
Capture lets me quickly make notes & capture ideas, with associated reference material, without any interruption to the current work flow. Without losing focus on what you're doing, quickly jot down a note of something important that just came up.
‘my/org-capture’ Implementation
(cl-defun my/org-capture-buffer (&optional keys no-additional-remarks (heading-regexp "Subject: \\(.*\\)")) "Capture the current [narrowed] buffer as a todo/note. This is mostly intended for capturing mail as todo tasks ^_^ When NO-ADDITIONAL-REMARKS is provided, and a heading is found, then make and store the note without showing a pop-up. This is useful for when we capture self-contained mail. The HEADING-REGEXP must have a regexp parenthesis construction which is used to obtain a suitable heading for the resulting todo/note." (interactive "P") (let* ((current-content (substring-no-properties (buffer-string))) (heading (progn (string-match heading-regexp current-content) (or (match-string 1 current-content) "")))) (org-capture keys) (insert heading "\n\n\n\n" (s-repeat 80 "-") "\n\n\n" current-content) ;; The overtly verbose conditions are for the sake of clarity. ;; Moreover, even though the final could have “t”, being explicit ;; communicates exactly the necessary conditions. ;; Being so verbose leads to mutual exclusive clauses, whence order is irrelevant. (cond ((s-blank? heading) (beginning-of-buffer) (end-of-line)) ((and no-additional-remarks (not (s-blank? heading))) (org-capture-finalize)) ((not (or no-additional-remarks (s-blank? heading))) (beginning-of-buffer) (forward-line 2) (indent-for-tab-command)))))
With that in-hand, we use a wrapper to org-capture
to make use of it.
(defun my/org-capture (&optional prefix keys) "Capture something! C-c c ⇒ Capture something C-u C-c c ⇒ Capture current [narrowed] buffer. C-u 5 C-c c ⇒ Capture current [narrowed] buffer without adding additional remarks. C-u C-u C-c c ⇒ Goto last note stored. At work, ‘C-c c’ just captures notes under ‘Tasks’; no menu used." (interactive "p") (pcase prefix (4 (my/org-capture-buffer keys)) (5 (my/org-capture-buffer keys :no-additional-remarks)) (t (if my/personal-machine? (org-capture prefix keys) (org-capture prefix "t")))))
- C-c c Capture something
- C-u C-c c Capture current [narrowed] buffer.
- C-u 5 C-c c Capture current [narrowed] buffer without adding additional remarks.
- C-u C-u C-c c Goto last note stored.
E.g., I have a task, or something I wish to note down, rather than opening some file, then making a heading, then writing it; instead, I press C-c c t and a pop-up appears, I make my note, and it disappears —with my notes file(s) now being altered! Moreover, by default it provides a timestamp and a link to the file location where I made the note —helpful for tasks, tickets, to be tackled later on.
;; Location of my todos / captured notes file (unless noninteractive (setq org-default-notes-file (if my/personal-machine? "~/Dropbox/todo.org" "~/Desktop/Work-2022-01-01.org"))) ;; “C-c c” to quickly capture a task/note (define-key global-map "\C-cc" #'my/org-capture) ;; See above.
By default we only get a ‘tasks’ form of capture, let's add some more.
(cl-defun my/make-org-capture-template (shortcut heading &optional (no-todo nil) (description heading) (scheduled nil)) "Quickly produce an org-capture-template. After adding the result of this function to ‘org-capture-templates’, we will be able perform a capture with “C-c c ‘shortcut’” which will have description ‘description’. It will be added to the tasks file under heading ‘heading’. ‘no-todo’ omits the ‘TODO’ tag from the resulting item; e.g., when it's merely an interesting note that needn't be acted upon. Default for ‘description’ is ‘heading’. Default for ‘no-todo’ is ‘nil’. Scheduled items appear in the agenda; true by default. The target is ‘file+headline’ and the type is ‘entry’; to see other possibilities invoke: C-h o RET org-capture-templates. The “%?” indicates the location of the Cursor, in the template, when forming the entry. " `(,shortcut ,description entry (file+headline org-default-notes-file ,heading) ,(concat "*" (unless no-todo " TODO") " %?\n" (when nil ;; this turned out to be a teribble idea. ":PROPERTIES:\n:" (if scheduled "SCHEDULED: %^{Any time ≈ no time! Please schedule this task!}t" "CREATED: %U") "\n:END:") "\n\n ") :empty-lines 1 :time-prompt t))
(setq org-capture-templates (cl-loop for (shortcut heading) in (-partition 2 '("t" "Tasks, Getting Things Done" "r" "Reference Material" "m" "Email" "e" "Emacs (•̀ᴗ•́)و" "i" "Islam" "b" "Blog" "a" "Arbitrary Reading and Learning" "l" "Programming Languages" "p" "Personal Matters")) collect (my/make-org-capture-template shortcut heading)))
Rather than adding notes to particular Org headings in my todo.org
file, I could
defer such a choice by having only one template and have C-c a
automatically use
it. Then I could ‘refile’ tasks to their appropriate parent headings with w
.
This allows us to seperate the concerns of capturing ideas from doing any form
of processing. Something to consider.
;; Update: Let's schedule tasks during the GTD processing phase. ;; ;; For now, let's automatically schedule items a week in advance. ;;;; (defun my/org-capture-schedule () ;; (org-schedule nil "+7d")) ;; ;; (add-hook 'org-capture-before-finalize-hook 'my/org-capture-schedule)
For now I capture everything into a single file. One would ideally keep separate client, project, information in its own org file.
- ⇒ Org capture actually lets us add any type of entry, ‘programmable template’,
to any type of file! ⇐
- Look at my/make-org-capture-template, above, to notice that capture actually lets you add any type of item to any file.
- ( For now, I'm only using it to add entries to my tasks lists. )
- Org-protocol is a way to create capture notes in org-mode from other applications.
Let's also ensure TODO-s respect hierarchical structure.
;; Cannot mark an item DONE if it has a TODO child. ;; Conversely, all children must be DONE in-order for a parent to be DONE. (setq org-enforce-todo-dependencies t)
Where am I currently capturing?
- During meetings, when a nifty idea pops into my mind, I quickly capture it.
- I've found taking my laptop to meetings makes me an active listener and I get much more out of my meetings since I'm taking notes.
- Through out the day, as I browse the web, read, and work; random ideas pop-up, and I capture them indiscriminately.
- I envision that for a phone call, I would open up a capture to make note of what the call entailed so I can review it later.
- Yet another place to capture content is from mail, such as for reference material, or self-contained tasks.
Anywhere you simply want to make a note, for the current heading, just press C-c C-z. The notes are just your remarks along with a timestamp; they are collected at the top of the tree, under the heading.
;; Ensure notes are stored at the top of a tree. (setq org-reverse-note-order nil)
Quickly look up some reference material…
(cl-defun my/reference (&optional (file org-default-notes-file)) "Look up some reference material super quick. By default we look in user's Org TODOs file. FILE should be an ORG file with a top-level heading that starts with ‘Reference’. We show its subheadings in a completing-read menu, then narrow to that entry." (interactive) (find-file file) (widen) (goto-char (point-min)) (re-search-forward "^* Reference") ;; Start of line. (org-narrow-to-subtree) (org-cycle) (org-cycle) (let* ((headings (org-map-entries (lambda () (org-element-property :title (org-element-at-point)) ) "LEVEL=2")) (topic (completing-read "What to review? " headings))) (search-forward (concat "** " topic)) (org-narrow-to-subtree) (org-cycle))) (defalias 'my/review-reference-notes 'my/reference) (defalias 'w-reference 'my/reference) ;; “w”ork
5.1.2. Step 1: When new tasks come up
Isn't it great that we can squirrel away info into some default location
then immediately return to what we were doing before —with speed & minimal distraction! ♥‿♥
Indeed, if our system for task management were slow then we may not produce
tasks and so forget them altogether! щ(゜ロ゜щ)
Entering tasks is a desirably impulsive act; do not make any further scheduling considerations.
The next step, the review stage occurring at the end or the start of the workday, is for processing.
The reason for this is that entering new tasks should be impulsive, not reasoned. Your reasoning skills are required for the task at hand, not every new tidbit. You may even find that during the few hours that transpire between creating a task and categorizing it, you’ve either already done it or discovered it doesn’t need to be done at all! ---John Wiegley
When my computer isn't handy, I'll make a note on my phone then transfer it later.
5.1.3. Step 2: Filing your tasks
At a later time, a time of reflection, we go to our tasks list and actually
schedule time to get them done by C-c C-s, org-schedule, then pick a
date by entering a number in the form +𝓃
to mean that task is due 𝓃
days from
now.
- Tasks with no due date are ones that “could happen anytime”, most likely no time at all.
- At least schedule tasks reasonably far off in the future, then reassess when the time comes.
An uncompleted task is by default rescheduled to the current day, each day, along with how overdue it is.
- Aim to consciously reschedule such tasks!
Let's keep track of how many times, and when, we have pushed events to other dates.
;; Add a note whenever a task's deadline or scheduled date is changed. (setq org-log-redeadline 'time) (setq org-log-reschedule 'time)
custard
With time, it will become clear what is an unreasonable day verses what is an achievable day.
Repeat tasks by a repeater such as ‘+1m’ or ‘+7d’ in their timestamps; e.g.,
:SCHEDULED: <2005-10-01 Sat +1m>.
Likewise, to schedule an event (to repeat) for
multiple days, just use a bunch of timestamps (with repeaters): :SCHEDULED:
<2022-01-04 Tues 9:00 +1w><2022-01-06 Thu +1w><2022-01-07 Fri 9:00 +1w>
.
(Notice that we have :
on each side of the keyword; and we have a time of day
for the event.)
A ‘project’ is a task that has multiple steps, each as a checkbox item. It can
be given a percentage marker to show progress: Place [%]
after its name, then
press C-c # ---org-update-statistics-cookies— on the name to see a
completion percentage —press C-c C-c on a checkbox item to toggle its
completion state.
5.1.4. Step 3: Quickly review the upcoming week
The next day we begin our work, we press C-c a a to see the scheduled tasks for this week ---C-c C-s to re-schedule the task under the cursor and r to refresh the agenda.
(define-key global-map "\C-ca" 'org-agenda)
- Show the next 𝓃 days schedule ⇐
C-u 𝓃 C-c a a
.
Org agenda is an interactive tool for generating summary reports from Org data —e.g., commonly, the weekly task list is generated from todo tasks.
The agenda dispatch menu, C-c a
, has options for displaying tasks —e.g., C-c a
m
generates a list of entries having the same tags. new ways to view tasks by
altering the org-agenda-custom-commands
variable —e.g., above we added two,
one for completed tasks and one for unscheduled tasks.
Let's setup the basics of our agenda.
;; List of all the files & directories where todo items can be found. Only one ;; for now: My default notes file. (setq org-agenda-files (list org-default-notes-file)) ;; Display tags really close to their tasks. (setq org-agenda-tags-column -10) ;; How many days ahead the default agenda view should look (setq org-agenda-span 'day) ;; May be any number; the larger the slower it takes to generate the view. ;; One day is thus the fastest ^_^ ;; How many days early a deadline item will begin showing up in your agenda list. (setq org-deadline-warning-days 14) ;; In the agenda view, days that have no associated tasks will still have a line showing the date. (setq org-agenda-show-all-dates t) ;; Scheduled items marked as complete will not show up in your agenda view. (setq org-agenda-skip-scheduled-if-done t) (setq org-agenda-skip-deadline-if-done t)
Super Simple ‘agenda’ Mini-tutorial
The agenda view, like nearly all Emacs entities, is interactive:
𝓃 f,b
⇒ Look forward at next week's agenda, or backward to a previous week.- The optional \(𝓃\) means do the action
𝓃
-many times; it defaults to 1.
- The optional \(𝓃\) means do the action
w, d
⇒ toggle week view, or day view; usev
to see possible views.- E.g.,
C-u 2017 v y
shows us the specific year 2017.
- E.g.,
𝓃 n,p
to navigate to next and previous entries.t
⇒ cycle TODO state of the current entry.±
⇒ cycle priority state.𝓃 S-⇆
⇒ Shift date time by \(𝓃\) days; 1 day by default.C-c C-s
⇒ Reschedule an entry; prefix it withC-u
to remove a scheduled entry.- Repeat tasks by a repeater such as ‘+1m’ or ‘+7d’
in their timestamps; e.g.,
DEADLINE: <2005-10-01 Sat +1m>.
s
⇒ save all agenda buffers; i.e., save the org-files where the agenda items live.g
⇒ Rebuild agenda according to any changes made thus far.F
⇒ Toggle ‘follow mode’: As you go up/down entries, you can see their details in an adjacent window.SPC
⇒ Show details of a single entry in other window; stay in Agenda.
RET, TAB
⇒ Go to the current entry in the current window or in a new adjacent window, so as to alter task details.
The agenda view ––even in the 7-days-at-a-time view–– will always begin on the current day. This is important, since while using org-mode as a day planner, you never want to think of days gone past. That’s something you do in other ways, such as when reviewing completed tasks.
(setq org-agenda-start-on-weekday nil) ;; Start each agenda item with ‘○’, then show me it's %timestamp and how many ;; times it's been re-%scheduled. (setq org-agenda-prefix-format " ○ %t%s")
Grouping agenda entries together
Instead of having the day's tasks all in one field, org-super-agenda allows us
to use predicates to group entries together; e.g., by considering an entry's
:tags:
or its priority level. Since I'm placing all my tasks in a single file,
under appropriate parent headings, I want entries to be shown according to their
parent heading. Of-course, the top-most grouping, the important tasks, should be
pulled out of their group and placed at the top.
(use-package origami) (use-package org-super-agenda :hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB. :bind (:map org-super-agenda-header-map ("<tab>" . origami-toggle-node)) :config (org-super-agenda-mode) (setq org-super-agenda-groups '((:name "Important" :priority "A") (:name "Personal" :habit t) ;; For everything else, nicely display their heading hierarchy list. (:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path))))))) ;; MA: No noticable effect when using org-super-agenda :/ ;; ;; Leave new line at the end of an entry. ;; (setq org-blank-before-new-entry '((heading . t) (plain-list-item . t)))
The org-super-agenda homepage shows complex configurations and pleasant screenshots contrasting with and without the system. E.g., you can change how entries in particular headings are displayed and coloured.
5.1.5. Step 4: Getting ready for the day
After having seen our tasks for the week, we press d to enter daily view for
the current day. Now we decide whether the items for today are A
: of high
urgency & important; B
: of moderate urgency & importance; or C
: Pretty much
optional, or very quick or fun to do.
A
tasks should be both important and urgently done on the day they were scheduled.- Such tasks should be relatively rare!
- If you have too many, you're anxious about priorities and rendering priorities useless.
C
tasks can always be scheduled for another day without much worry.- Act! If the thought of rescheduling causes you to worry, upgrade it to a
B
orA
.
- Act! If the thought of rescheduling causes you to worry, upgrade it to a
- As such, most tasks will generally be priority
B
: Tasks that need to be done, but the exact day isn't as critical as with anA
task. These are the “bread and butter” tasks that make up your day to day life.
On a task item, or any org-heading, press , then one of A/B/C to set its priority. Then r to refresh.
Pretty Prioritisation Markers
Let's set four priority levels and their colours: The more intense colours are for more urgent tasks.
(setq org-lowest-priority ?C) ;; Now org-speed-eky ‘,’ gives 3 options (setq org-priority-faces '((?A :foreground "red" :weight bold) ;; :background "LightCyan1") (?B :foreground "orange" :weight bold) (?C :foreground "green" :weight bold))) ;; See all colours with: M-x list-colors-display
C-c ,
anywhere to set the priority of the current heading.- We may press
A-D
orSPC
to an remove existing priority.
- We may press
Priority markers are of the form [#𝒳]
, the fancy priorities package visually
renders them as words or icons.
(use-package org-fancy-priorities :diminish org-fancy-priorities-mode :hook (org-mode . org-fancy-priorities-mode) :custom (org-fancy-priorities-list '("High" "MID" "LOW")) ;; "OPTIONAL" ;; Let's use the “Eisenhower map of priority”… ;; :custom (org-fancy-priorities-list '("Urgent and Important" ;; Do now! ;; "Not Urgent But Important" ;; Do schedule this. ;; "Urgent But Not Important" ;; Delegate? ;; "Not Urgent and Not Important")) ;; Don't do / Optional )
At work, my tasks list is massive and constantly growing; so to avoid stressing
myself out, I try to add time effort estimates to my tasks with
org-set-effort then I open my Org agenda and run d
(for day view) then
org-agenda-columns (C-c C-x C-c
) to see a column-view of my time estimates
along with a total time estimate for the day. Since there are unexpected
pair-programming calls, or meetings go longer than expected, I only schedule for
7 hours each day (in a usual 8-hour work day); i.e., if my time estimates exceed
7h then I reschedule or cancel some things.
(require 'org-agenda) ;; How should the columns view look? (setq org-columns-default-format "%60ITEM(Task) %6Effort(Estim){:} %3PRIORITY %TAGS") ;; Press “c” in Org agenda to see the columns view; (default binding C-c C-x C-c is too long!) (org-defkey org-agenda-mode-map "c" #'org-agenda-columns) (org-defkey org-agenda-mode-map "C" #'org-agenda-goto-calendar) ;; Press “e” in columns view to alter “e”ffort “e”stimates. (require 'org-colview) (org-defkey org-columns-map "e" ;; Refresh after making an effort estimate. (lambda () (interactive) (org-agenda-set-effort) (org-agenda-columns)))
Near the top of my TODOS file, I have the following clickable link: It looks pretty and when I click it, takes me where I need to go.
[[elisp:(progn (org-agenda nil "a") (org-agenda-columns) (delete-other-windows))][Show agenda with time estimates]]
5.1.6. Step 5: Doing the work
Since A
tasks are the important and urgent ones, if you do all of the A
tasks and
nothing else today, no one would suffer. It's a good day (─‿‿─).
There should be no scheduling nor prioritising at this stage. You should not be touching your tasks file until your next review session: Either at the end of the day or the start of the next.
- Leverage priorities! E.g., When a full day has several
C
tasks, reschedule them for later in the week without a second thought.- You've already provided consideration when assigning priorities.
5.1.7. Step 6: Moving a task toward completion
My workflow states are described in the section
5.5 and contain states: TODO, STARTED, WAITING, ON_HOLD, CANCELLED, DONE
.
- Tasks marked
WAITING
are ones for which we are awaiting some event, like someone to reply to our query. As such, these tasks can be rescheduled until I give up or the awaited event happens —in which case I go toSTARTED
and document the reply to my query. - The task may be put off indefinitely with
ON_HOLD
, or I may choose never to do it withCANCELLED
. Along withDONE
, these three mark a task as completed and so it needn't appear in any agenda view.
I personally clock-in and clock-out of tasks —keep reading—, where upon clocking-out I'm prompted for a note about what I've accomplished so far. Entering a comment about what I've done, even if it's very little, feels like I'm getting something done. It's an explicit marker of progress.
In the past, I would make a “captain's log” at the end of the day, but that's like commenting code after it's written, I didn't always feel like doing it and it wasn't that important after the fact. The continuous approach of noting after every clock-out is much more practical, for me at least.
5.1.8. Step 7: Archiving Tasks
During the review state, when a task is completed, ‘archive’ it with
org-archive-subtree or C-c C-x C-s: This marks it as done, adds a time
stamp, and moves it to a local *.org_archive
file. What was our ‘to do’ list
becomes a ‘ta da’ list showcasing all we have done (•̀ᴗ•́)و
Archiving keeps task lists clutter free, but unlike deletion it allows us,
possibly rarely, to look up details of a task or what tasks were completed in a
certain time frame —which may be a motivational act, to see that you have
actually completed more than you thought, provided you make and archive tasks
regularly. We can use M-x org-search-view
to search an org file and the
archive file too, if we enable it so.
;; C-c a s ➩ Search feature also looks into archived files. ;; Helpful when need to dig stuff up from the past. (setq org-agenda-text-search-extra-files '(agenda-archives))
;; enables the org-agenda variables. (require 'org-agenda) ;; Need this to have “org-agenda-custom-commands” defined. (unless noninteractive ;; ➩ Show my agenda upon Emacs startup. (org-agenda "a" "a"))
Let's install some helpful views for our agenda.
C-c a c
: See completed tasks at the end of the day and archive them.;; Pressing ‘c’ in the org-agenda view shows all completed tasks, ;; which should be archived. (add-to-list 'org-agenda-custom-commands '("c" todo "DONE|ON_HOLD|CANCELLED" nil))
C-c a u
: See unscheduled, undeadlined, and undated tasks in my todo files. Which should then be scheduled or archived.(add-to-list 'org-agenda-custom-commands '("u" alltodo "" ((org-agenda-skip-function (lambda () (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp "\n]+>"))) (org-agenda-overriding-header "Unscheduled TODO entries: "))))
At the end of the day, let's schedule at least 3 things that must be done the
next day; i.e., have priority A
.
5.2. Tag! You're it!
Even when items are categorised under their own parent headings, they may be
related in some way and that can made explicit by adding a :tag:
to their
headings; e.g., two entries both have the :jasim:@work:
tags, then looking for
the :@work:
tag shows me all entries that are tagged as “at work”.
Tags provide a cross-section of one's entries. |
Tags let us find related stuff quickly, even though they're differently categorised.
After calling org-agenda
, we may select m
to match for tags, or use
org-tags-view
to search for tags.
What to tag? Common tags are :@laptop:, :@work:, :@home:
to identify the
location where tasks take place —Use: When I'm at a particular place, I need
only consider tasks that apply to that place ;-) Other tags I use are :𝑭𝑳:
to
identify remarks or email or request from person 𝑭irstname 𝑳astname; or
something that might be interesting to that person. I also use :video:, :book:,
:paper:
; which let me quickly find all videos! Finally, I also use
:project_name:
to identify notes that may be of interest to a particular
project, but are more appropriately categorised elsewhere —e.g., when learning
about an Emacs feature, I may tag my notes with another project's name to
consider whether that feature could be useful there.
How to tag?
You can just add a :tag₁:⋯:tagₙ:
after a heading. If you press space, before the
tags, then they are automatically indented flushright to column 77; postive
numbers do not flushright but use exact column number.
(setq org-tags-column -77) ;; the default
Use C-c C-q
, or org-set-tags-command
, on a heading or just the speed key :
on
the asterisks of a heading to set the tags of an item —as usual, with Helm we
obtain a window of all existing tags to select from. Unfortunatley, this only
supports having one tag; for more, you can add them in manually or …
;; Press ‘:’ on a heading to add a tag, press TAB to see all tags, press RETURN on a tag to add it, press TAB again to add more tags, when all done press RETURN twice. (use-package helm-org) ;; Helm for org headlines and keywords completion. (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command . helm-org-completing-read-tags)) ;; Also provides: helm-org-capture-templates
Now :
or C-c C-q
will show existing tags for the current heading, press TAB
to
obtain a list of all exisiting tags, press C-SPC
to select the desired tags,
then TAB
or RET
to confirm the resulting tag list, and RET
to finish or TAB
to
select more tags.
Let's render tags by Unicode symbols.
(use-package org-pretty-tags :diminish org-pretty-tags-mode :demand t :config (setq org-pretty-tags-surrogate-strings '(("Neato" . "💡") ("Blog" . "✍") ("Audio" . "♬") ("Video" . "📺") ("Book" . "📚") ("Running" . "🏃") ("Question" . "❓") ("Wife" . "💕") ("Text" . "💬") ; 📨 📧 ("Friends" . "👪") ("Self" . "🍂") ("Finances" . "💰") ("Car" . "🚗") ; 🚙 🚗 🚘 ("Urgent" . "🔥"))) ;; 📥 📤 📬 (org-pretty-tags-global-mode 1))
5.3. Automating Pomodoro —“Commit for only 25 minutes!”
Effort estimates are for an entire task. Yet, sometimes it's hard to even get started on some tasks.
- The code below ensures a 25 minute timer is started whenever clocking in happens.
- The timer is in the lower right of the modeline.
- When the timer runs out, we get a notification.
- We may have the momentum to continue on the difficult task, or clock-out and take a break after documenting what was accomplished.
;; Tasks get a 25 minute count down timer (setq org-timer-default-timer 25) ;; Use the timer we set when clocking in happens. (add-hook 'org-clock-in-hook (lambda () (org-timer-set-timer '(16)))) ;; unless we clocked-out with less than a minute left, ;; show disappointment message. (add-hook 'org-clock-out-hook (lambda () (unless (s-prefix? "0:00" (org-timer-value-string)) (message-box "The basic 25 minutes on this difficult task are not up; it's a shame to see you leave.")) (org-timer-stop)))
Note that this does not conflict with the total effort estimate for the task.
(I'm told there's a package already made for this —maybe I need to stop writing code, and do more searches; then again, I've learned a lot by writing code.)
5.4. Journaling
Thus far I've made it easy to quickly capture ideas and tasks, not so much on the analysis phase:
- What was accomplished today?
- What are some notably bad habits? Good habits?
- What are some future steps?
Rather than overloading the capture mechanism for such thoughts, let's employ
org-journal
—journal entries are stored in files such as journal/20190407
,
where the file name is simply the date, or only one file per year as I've set it
up below. Each entry is the week day, along with the date, then each child tree
is an actual entry with a personal title preceded by the time the entry was
made. Unlike capture and its agenda support, journal ensures entries are
maintained in chronological order with calendar support.
Since org files are plain text files, an entry can be written anywhere and later ported to the journal. Or, written directly in the journal file if we add the necessary Org-header: Asterisks and time.
The separation of concerns is to emphasise the capture stage as being quick and relatively mindless, whereas the journaling stage as being mindful. Even though we may utilise capture to provide quick support for including journal entries, I have set my journal to be on a yearly basis —one file per year— since I want to be able to look at previous entries when making the current entry; after all, it's hard to compare and contrast easily unless there's multiple entries opened already.
As such, ideally at the end of the day, I can review what has happened, and what has not, and why this is the case, and what I intend to do about it, and what problems were encountered and how they were solved —in case the problem is encountered again in the future. Consequently, if I encounter previously confronted situations, problems, all I have to do is reread my journal to get an idea of how to progress. Read more about the importance of reviewing your day on a daily basis.
Moreover, by journaling with Org on a daily basis, it can be relatively easy to produce a report on what has been happening recently, at work for example. I'd like to have multiple journals, for work and for personal life, as such I will utilise a prefix argument to obtain my work specific entries.
5.4.1. The Setup
Anyhow, the setup:
(use-package org-journal ;; C-u C-c j ⇒ Work journal ;; C-c j ⇒ Personal journal :bind (("C-c j" . my/org-journal-new-entry)) :config (setq org-journal-dir "~/Desktop/" ;; "~/Dropbox/journal/" org-journal-file-type 'yearly org-journal-file-format "Personal-%Y-%m-%d.org") (defun my/org-journal-new-entry (prefix) "Open today’s journal file and start a new entry. With a prefix, we use the work journal; otherwise the personal journal." (interactive "P") (let ((org-journal-dir (if prefix "~/Desktop/" org-journal-dir)) (org-journal-file-format (if prefix "Work-%Y-%m-%d.org" org-journal-file-format))) (org-journal-new-entry nil) (org-mode) (org-show-all))))
5.4.2. Super Terse Tutorial
Bindings available in org-journal-mode
, when journaling:
C-c C-j
: Insert a new entry into the current journal file.- Note that keys for
org-journal-new-entry
shadow those fororg-goto
.
- Note that keys for
C-c C-s
: Search the journal for a string.- Note that keys for
org-journal-search
shadow those fororg-schedule
.
- Note that keys for
All journal entries are registered in the Emacs Calendar. To see available
journal entries do M-x calendar
. Bindings available in the calendar-mode:
j
: View an entry in a new buffer.i j
: ‘I’nsert a new ‘j’ournal entry into the day’s file.f w/m/y/f/F
: ‘F’ind, search, in all entries of the current week, month, year, all of time, of in all entries in the future.
All journal entries are registered in the Emacs Calendar. To see available journal entries do M-x calendar. Bindings available in the calendar-mode:
- j d display an entry; use j r to jump to the new reading buffer; reading is in view-mode: q to quit reading and SPC to scroll.
- j s w/m/y/f search the journal entries of the current week/month/year or for all time
[/]
go the previous/next day with journal entries
5.4.3. Guided Journaling
Sometimes it can be tough to journal, but filling in a template can be a way to
get started. Later on, we will setup 4.5 which will
allow us to write journal_guided
then TAB
to obtain the template below. Each $𝓃
indicates a position that we may input text, after which we TAB
to move to next
location.
Just like the undo-tree
setup at the start of this read, we use a noweb-ref
to present this template in a natural position; then later when template
expansion it setup, we request it to be tangled.
** journal_guided: Introspection & Growth I'm writing from ${1:location}. Gut answer, today I feel ${2:scale}/10. ⇒ ${3:Few words or paragraphs to explain what's on your mind.} ${4: All things which cause us to groan or recoil are part of the tax of life. These things you should never hope or seek to escape. Life is a battle, and to live is to fight. ⟨ Press TAB once you've read this mantra. ⟩ $(when yas-moving-away-p "") } `(progn (eww "https://www.dailyinspirationalquotes.in/") (sit-for 2) (when nil let eww load) (read-only-mode -1) (goto-line 52) (kill-line) (kill-buffer) (yank))` ${7: Self Beliefs: + I am working on a healthier lifestyle, including a low-carb diet. - I’m also investing in a healthy, long-lasting relationship. ➩ These are what I want and are important to me. ⇦ + I will not use any substances to avoid real issues in my life. I must own them. + Everything I’m searching for is already inside of me. + Progress is more important than perfection. ⟨ Press TAB once you've read these beliefs. ⟩ $(when yas-moving-away-p "") } *Three things I'm grateful for:* 1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} 2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} 3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} *Three things that would make today great:* 1. ${11:???} 2. ${12:???} 3. ${13:???} *What one thing is top of mind today?* ${14:???} *What’s one opportunity I want to go after?* ${15:???} *What’s one thing I’m really proud of OR I’m amazed and in awe of?* ${16:???} $0
Besides a bit of webscraping to obtain a daily inspirational quote image, and the necessary yasnippet code, this template was taken from a discussion on Hacker news: “I find journaling indispensable”. In time, I will likely alter it to meet my needs, but I like it as it is right now (•̀ᴗ•́)و
5.5. Workflow States
Here are some of my common workflow states, —the ‘X/Y’ indicates to do action ‘X’ when entering a state and ‘Y’ when leaving it, with ‘!’ denoting a timestamp should be generated and ‘@’ denoting a user note should be made.
(setq org-todo-keywords '((sequence "TODO(t)" "STARTED(s@/!)" "|" "DONE(d/!)") (sequence "WAITING(w@/!)" "ON_HOLD(h@/!)" "|" "CANCELLED(c@/!)"))) ;; Since DONE is a terminal state, it has no exit-action. ;; Let's explicitly indicate time should be noted. (setq org-log-done 'time)
The @
brings up a pop-up to make a local note about why the state changed.
Super cool stuff!
In particular, we transition from TODO
to STARTED
once 15 minutes, or a
reasonable amount, of work has transpired. Since all but one state are marked
for logging, we could use the lognotestate
logging facility of org-mode, which
prompts for a note every time a task’s state is changed.
Entering a comment about what I've done, even if it's very little, feels like
I'm getting something done. It's an explicit marker of progress and motivates me
to want to change my task's states more often until I see it marked DONE
.
Here's how they are coloured,
(setq org-todo-keyword-faces '(("TODO" :foreground "red" :weight bold) ("STARTED" :foreground "blue" :weight bold) ("DONE" :foreground "forest green" :weight bold) ("WAITING" :foreground "orange" :weight bold) ("ON_HOLD" :foreground "magenta" :weight bold) ("CANCELLED" :foreground "forest green" :weight bold)))
Now we press C-c C-t
then the letter shortcut to actually make the state of an org heading.
(setq org-use-fast-todo-selection t)
We can also change through states using Shift- left, or right.
Let's draw a state diagram to show what such a workflow looks like.
PlantUML supports drawing diagrams in a tremendously simple format —it even supports Graphviz/DOT directly and many other formats. Super simple setup instructions can be found here; below are a bit more involved instructions. Read the manual here.
;; Install the tool ; (async-shell-command "brew tap adoptopenjdk/openjdk; brew cask install adoptopenjdk13") ;; Dependency ; (async-shell-command "brew install plantuml") ;; Tell emacs where it is. ;; E.g., (async-shell-command "find / -name plantuml.jar") (setq org-plantuml-jar-path "/usr/local/Cellar/plantuml/1.2020.19/libexec/plantuml.jar") ;; Enable C-c C-c to generate diagrams from plantuml src blocks. (add-to-list 'org-babel-load-languages '(plantuml . t) ) (require 'ob-plantuml) ; Use fundamental mode when editing plantuml blocks with C-c ' (add-to-list 'org-src-lang-modes '("plantuml" . fundamental))
Let's use this!
skinparam defaultTextAlignment center /' Text alignment '/ skinparam titleBorderRoundCorner 15 skinparam titleBorderThickness 2 skinparam titleBorderColor red skinparam titleBackgroundColor Aqua-CadetBlue title My Personal Task States [*] -> Todo /' This is my starting state '/ Done -right-> [*] /' This is an end state '/ Cancelled -up-> [*] /' This is an end state '/ /'A task is “Todo”, then it's “started”, then finally it's “done”. '/ Todo -right-> Started Started -down-> Waiting Waiting -up-> Started Started -right-> Done /'Along the way, I may pause the task for some reason then return to it. This may be since I'm “Blocked” since I need something, or the task has been put on “hold” since it may not be important right now, and it may be “cancelled” eventually. '/ Todo -down-> Waiting Waiting -up-> Todo Waiting -up-> Done Todo -down-> On_Hold On_Hold -> Todo On_Hold -down-> Cancelled Waiting -down-> Cancelled Todo -down-> Cancelled /' The Org-mode shortcuts for these states are as follows. '/ Todo : t On_Hold : h Started : s Waiting : w Cancelled : c Done : d /' If a task is paused, we should document why this is the case. '/ note right of Waiting: Note what is\nblocking us. note right of Cancelled: Note reason\nfor cancellation. note bottom of On_Hold: Note reason\nfor reduced priority. center footer ♥‿♥ Org-mode is so cool (•̀ᴗ•́)و /' Note that we could omit the “center, left, right” if we wished, or used a “header” instead.'/
Of note:
- Multiline comments are with
/' comment here '/
, single quote starts a one-line comment. - Nodes don't need to be declared, and their names may contain spaces if they are enclosed in double-quotes.
One forms an arrow between two nodes by writing a line with
x ->[label here] y
ory <- x
; or using-->
and<--
for dashed lines. The label is optional.To enforce a particular layout, use
-X->
whereX ∈ {up, down, right, left}
.- To declare that a node
x
has fieldsd, f
we make two new lines havingx : f
andx : d
. One adds a note near a node
x
as follows:note right of x: words then newline\nthen more words
.Likewise for notes on the
left, top, bottom
.- A note can be on several lines. It's terminated by
end note
.
- A note can be on several lines. It's terminated by
- Interesting sprites and many other things can be done with PlantUML. Read the docs.
This particular workflow is inspired by Bernt Hansen —while quickly searching through the PlantUML manual: The above is known as an “activity diagram” and it's covered in §4.
Org-mode may be used with PlantUML:
- See §11,12 for using Org-mode notation to form ‘mindmaps’ and ‘work breakdown structures’.
- Org-mode text formatters are also acknowledged but the delimiters must be doubled; see §16.1.
You can quickly write and see the resulting UMLs using https://liveuml.com/, for the most part.
5.6. Clocking Work Time
Let's keep track of the time we spend working on tasks that we may have captured for ourselves the previous day. Such statistics provides a good idea of how long it actually takes me to accomplish a certain task in the future and it lets me know where my time has gone.
- Clock in
- on a heading with
I
, or in the subtree withC-c C-x C-i
. - Clock out
- of a heading with
O
, or in the subtree withC-c C-x C-o
. - Clock report
- See clocked times with
C-c C-x C-r
.
After clocking out, the start and end times, as well as the elapsed time, are added to a drawer to the heading. We can punch in and out of tasks as many times as desired, say we took a break or switched to another task, and they will all be recorded into the drawer.
;; Record a note on what was accomplished when clocking out of an item. (setq org-log-note-clock-out t)
To get started, we could estimate how long a task will take and clock-in; then clock-out and see how long it actually took.
Sometimes, at the beginning at least, I would accidentally invoke the transposed
command C-x C-c
, which saves all buffers and quits Emacs. So here's a helpful
way to ensure I don't quit Emacs accidentally.
(setq confirm-kill-emacs 'yes-or-no-p)
A few more settings:
;; Resume clocking task when emacs is restarted (org-clock-persistence-insinuate) ;; Show lot of clocking history (setq org-clock-history-length 23) ;; Resume clocking task on clock-in if the clock is open (setq org-clock-in-resume t) ;; Sometimes I change tasks I'm clocking quickly ---this removes clocked tasks with 0:00 duration (setq org-clock-out-remove-zero-time-clocks t) ;; Clock out when moving task to a done state (setq org-clock-out-when-done t) ;; Save the running clock and all clock history when exiting Emacs, load it on startup (setq org-clock-persist t) ;; Do not prompt to resume an active clock (setq org-clock-persist-query-resume nil) ;; Include current clocking task in clock reports (setq org-clock-report-include-clocking-task t)
5.6.1. Finding tasks to clock in
Use one of the following options, with the top-most being the first to be tried.
- From anywhere,
C-u C-c C-x C-i
yields a pop-up for recently clocked in tasks. - Pick something off today's agenda scheduled items.
- Pick a
Started
task from the agenda view, work on this unfinished task. - Pick something from the
TODO
tasks list in the agenda view.
C-c C-x C-d
also provides a quick summary of clocked time for the current org file.
5.6.2. Estimates versus actual time
Before clocking into a task, add to the properties drawer :Effort: 1:25
or C-c
C-x C-e
, for a task that you estimate will take an hour and twenty-five minutes,
for example. Now the modeline will mention the time elapsed alongside the task
name. Woah!
(push '("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") org-global-properties)
Use speed keys
e/E
to insert an effort estimate, with the above being provided options, or to increment the current effort to the next one in the above list.
This is also useful when you simply want to put a time limit on a task that wont be completed anytime soon, say writing a thesis or a long article, but you still want to work on it for an hour a day and be warned when you exceed such a time constraint.
When you've gone above your estimate time, the modeline colours it red.
5.7. Habit Formation
The reason to use habits is that they come with a graph indicating consistency by colour, and the goal of the game is to have the longest possible chain —no red days!
A ‘habit’ is a usual (recurring) todo task marked as a habit:
Use C-c C-x p
to set the STYLE
property to habit
on a task to set it as a habit.
;; Show habits for every day in the agenda. (setq org-habit-show-habits t) (setq org-habit-show-habits-only-for-today nil) ;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading. (setq org-habit-graph-column 90) ;; In order to see the habit graphs, which I've placed rightwards, let's ;; always open org-agenda in ‘full screen’. ;; (setq org-agenda-window-setup 'only-window)
inch by inch anything's a cinch! |
!
means today and ⋆
means a task has been done on that day;
intuitively green means you're on track, yellow is warning sign of overdue,
red is overdue, and blue is an acceptable break day.
Here's an example habit from the Org-mode manual, where .+𝒳d/𝒴d
reads
perform the habit once every 𝒳 days, but never let me go 𝒴 days without doing it.
** TODO Shave SCHEDULED: <2020-01-08 Wed .+2d/4d> :PROPERTIES: :STYLE: habit :END:
Shave every 2 days, but we can take a 3-day break; however, on the 4th day, gotta shave! (To “ignore” a habit, just reschedule it for another day.)
Remember that in the agenda view if you alter a task, say with t
to mark it
done, then you need to use s
to save the underlying todo/notes files; otherwise,
any g
will revert the change in the agenda buffer.
5.8. Actually Doing Things —or Sending notifications from Emacs
Let's setup a little audio-visual reminder to regularly check my agenda and ensure I'm not narrowing on a single task and ignoring others.
- More generally, we can use this snippet of code to let Emacs notify us of other things.
- For instance, the message function shows text in the minibuffer, which might be missed when there are multiple incoming messages or focus is on a non-Emacs application (gasp!). Then, my/notify could be used to produce MacOS system-wide notifications.
The text-to-speech tool we'll use is say
; which on a Mac can be activated
in a browser: Select some text, right-click, select Speech
, then Start/Stop Speaking
.
- TODO: Make the command below randomly use an English speaking voice; “tldr say” to learn more.
- TODO: Also finish reading the above mentioned links, with nice examples.
;; Obtain a notifications and text-to-speech utilities (system-packages-ensure "say") ;; Built-into MacOS, but can be downloaded in Ubuntu (system-packages-ensure "terminal-notifier") ;; MacOS specific ;; System Preferences → Notifications → Terminal Notifier → Allow “alerts”. ;; E.g.,: (shell-command "terminal-notifier -title \"Hiya\" -message \"hello\"")
By default, notifications are in banner style —they go away automatically—
we can use alert style —in which they stay until dismissed— in MacOS as
follows: System Preferences → Notifications → terminal-notifier → Alerts
.
(cl-defun my/notify (message &key (titled "") at repeat-every-hour open) "Notify user with both an visual banner, with a beep sound, and a text-to-speech recitation. When the user clicks on the resulting notification, unless a given OPEN url is provided, the Emacs application is brough into focus. MESSAGE and TITLE are strings; AT is a time string as expected of `run-at-time' such as \"11.23pm\" or \"5 sec\"; REPEAT-EVERY-HOUR is a floating-point number of hours to continuously repeat the alert. OPEN is a URL that is opened when the user clicks the notification. This can be a web or file URL, or any custom URL scheme. I initially used optional arguments, but realised that in due time it would be more informative to use named arguments instead. Example uses: ;; In 5 minutes from now, remind me to watch this neato video! (my/notify \"🔔 Get things done! 📎 💻 \" :open \"https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity\" :at \"5 minutes\") ;; Remind me to exercise every 1.5hours; starting at 8:00am. (my/notify \"Take a 5min break and get your blood flowing!\" :titled \"Exercise\" :at \"8:00am\" :repeat-every-hour 1.5) ;; Actually getting things done! (my/notify \"Is what you're doing actually in alignment with your goals? Maybe it's time to do another task?\" :titled \"Check your agenda!\" :at \"10:00am\" :repeat-every-hour 2) " (run-at-time at ;; the time to make the alert (when repeat-every-hour (* 60 60 repeat-every-hour)) #'async-shell-command (format "%s" `(terminal-notifier -title ,(pp-to-string titled) -message ,(s-replace "\\n" "\n" (pp-to-string message)) ;; Play a random sound when the notification appears. See sound names with: ls /System/Library/Sounds ;; Use the special NAME “default” for the default notification sound. -sound ,(progn (require 'seq) (seq-random-elt (s-split "\n" (shell-command-to-string "ls /System/Library/Sounds")))) ;; Don't create duplicates of the notification, just one instance; ;; i.e., each notification belongs to a group and only one alert of the group may be present at any one time. -group ,(pp-to-string titled) ;; Activate the application specified by ID when the user clicks the notification. -activate org.gnu.Emacs ,@(when open `(-open ,(pp-to-string open))) ;; Run the shell command COMMAND when the user clicks the notification. ;; -execute COMMAND & ;; … and then speak! … ;;;; say ,(s-replace "\\n" " " (pp-to-string message)) ))))
The following two actual uses cases are also mentioned in my/notify docstring, since I want the documentation to be self-contained.
;; (Emojis look terrible in Lisp; but much better when the alert is actually made!) ;; Remind me to exercise every 1.5hours; starting at 8:00am. (my/notify "Take a 5min break and get your blood flowing!\n\t\t🚣 🏃♂️ 🧗♂️ 🧘♂️ 🏊 🏋 🚴♂️" :titled "🤾♀️ Exercise 🚵♂️" :at "8:00am" :repeat-every-hour 1.5 :open "https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity") ;; Actually getting things done! (my/notify "Is what you're doing actually in alignment with your goals? ✔️📉 Maybe it's time to do another task? 📋" :titled "📆 Check your agenda! 🔔" :at "10:00am" :repeat-every-hour 2)
- Here is an approach to triggering audio-visual alarms from Org-mode events
—using
org-agenda-to-appt
. - Emacs's built in appointment notification facility can also be used as a alarm
clock via
M-x appt-add
.
The marquee-header package let's show messages as “horizontal moving text along the top of the Emacs frame”, which is neat.
- Slightly related is logms, which let's us make
message
calls but we can also see the context/source where those calls were made; as well as a clickable link back to the source.
6. Cosmetics
Upon startup, we want to be greeted with a useful, yet unobtrusive, message
briefly detailing major system details. Moreover, the bottom-most area of the
screen should display battery life, data, & time. Likewise, we may have a casual
file explorer —primarily to show-off to newcomers, since great functionality
is found with M-x dired
---dired.
;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★ ;; https://github.com/emacsorphanage/org-bullets (use-package org-bullets :hook (org-mode . org-bullets-mode))
6.1. Startup message: Emacs & Org versions
Let's always welcome ourselves when Emacs begins with a helpful message. For example, which user account is running and what are the version numbers of our primary tools.
;; Silence the usual message: Get more info using the about page via C-h C-a. (setq inhibit-startup-message t) (defun display-startup-echo-area-message () "The message that is shown after ‘user-init-file’ is loaded." (message (concat "Welcome " user-full-name "! Emacs " emacs-version "; Org-mode " org-version "; System " (symbol-name system-type) "/" (system-name) "; Time " (emacs-init-time))))
Now my startup message is,
Welcome Musa Al-hassy! Emacs 27.1; Org-mode 9.4.4; System darwin/Musas-MacBook-Air.local; Time 13.331914 seconds
Let's change the Emacs frame to mention the name of the buffer in focus, as well as a nice ‘motto’:
;; Keep self motivated! (setq frame-title-format '("" "%b - Living The Dream (•̀ᴗ•́)و"))
6.2. My to-do list: The initial buffer when Emacs opens up
I almost always have Emacs open; I don't need a dashboard, but would like to see my to-do list and my init file, side-by-side.
(unless noninteractive ;; Only run the following when we're in GUI mode; ;; i.e., don't run it in Github Actions when testing. (if my/personal-machine? (find-file "~/Dropbox/todo.org") ;; After startup, if Emacs is idle for 10 seconds, then open my work file; ;; which is a GPG file and so requires passphrase before other things can load. ;; (run-with-idle-timer 10 nil (lambda () (find-file "~/Desktop/work.org.gpg"))) (find-file "~/Desktop/Work-2022-01-01.org")) ;; Org-journal for work (split-window-right) ;; C-x 3 (other-window 1) ;; C-x 0 (let ((enable-local-variables :all) ;; Load *all* locals. (org-confirm-babel-evaluate nil)) ;; Eval *all* blocks. (ignore-errors (find-file "~/.emacs.d/init.org"))))
There is the neat-looking emacs-dashboard package that provides an extensbile yet minimalist splash screen showing recent files, projects, and bookmarks.
6.3. A sleek, informative, & fancy mode line
The ‘modeline’ is a part near the bottom of Emacs that gives information about the current buffer, such as its file-type/‘major-mode’ and enabled extensions/‘minor-modes’. Let's use the doom-modeline, which is a sleek & minimalistic, yet fancy setup with the following notable perks:
- Gives each buffer a nice icon in the modeline (denoting its major mode; e.g., Lisp/JavaScript/Org/etc each get a cool icon).
- Name of file becomes red when unsaved/modified.
- Nice version control icon, with branch name.
- Name of file is of the shape is shown as “project/file.ext”, when a project
is detected using
projectile.el
. - Flycheck error reporting is ugly by default, and one would consider using flycheck-status-emojis to make things look better in a simple modeline, but Doom-modeline gives a nice status indicators for Flycheck.
- Shows “+2” when the text scale is two above usual.
For fine-grained control on what/how things appear, there is doom-modeline-def-modeline and doom-modeline-set-modeline.
;; This package requires the fonts included with all-the-icons to be installed. Run M-x all-the-icons-install-fonts to do so. ;; The modeline looks really nice with doom-themes, e.g., doom-solarised-light. (use-package doom-modeline :config (doom-modeline-mode)) ;; Use minimal height so icons still fit; modeline gets slightly larger when ;; buffer is modified since the "save icon" shows up. Let's disable the icon. ;; Let's also essentially disable the hud bar, a sort of progress-bar on where we are in the buffer. (setq doom-modeline-height 1) (setq doom-modeline-buffer-state-icon nil) (setq doom-modeline-hud t) (setq doom-modeline-bar-width 1) ;; Show 3 Flycheck numbers: “red-error / yellow-warning / green-info”, which ;; we can click to see a listing. ;; If not for doom-modeline, we'd need to use flycheck-status-emoji.el. (setq doom-modeline-checker-simple-format nil) ;; Don't display the buffer encoding, E.g., “UTF-8”. (setq doom-modeline-buffer-encoding nil) ;; Inactive buffers' modeline is greyed out. ;; (let ((it "Source Code Pro Light" )) ;; (set-face-attribute 'mode-line nil :family it :height 100) ;; (set-face-attribute 'mode-line-inactive nil :family it :height 100))
6.3.1. Menu to Toggle Minor Modes: A quick way to see all of my modes, and which are enabled
Enabled minor modes clutter up the modeline with their names, albeit some have useful status information shown. We can either selectively pick which names/status are shown using diminish.el, possibly forgetting which minor modes are enabled or we can use minions.el to “gather up” all enabled minor modes, and recently enabled ones, under a single menu which doom-modeline shows as a simple configurations gear icon. ⚙. :gear:
(setq doom-modeline-minor-modes t) (use-package minions :init (minions-mode)) ;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list. ;; E.g.: (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max))))) ;; We likely want to add this locally, to hooks on major modes.
6.3.2. Nice battery icon alongside with percentage, in doom-modeline
;; If not for doom-modeline, we'd need to use fancy-battery-mode.el. (display-battery-mode +1)
[Posterity / Disabled] Fancy Battery Setup
Let's have it also show remaining battery life, coloured green if charging and coloured yellow otherwise. It is important to note that this package is no longer maintained. It works on my machine.
;; Let's use a fancy indicator … (use-package fancy-battery :diminish :custom (fancy-battery-show-percentage t) (battery-update-interval 15) :config (fancy-battery-mode))
6.3.3. Time & date
Let’s display the current time, with updates every second.
;; Show date and time as well. ;; [Simple Approach] ;; (setq display-time-day-and-date t) ;; (display-time) ;; [More Controlled Approach: Set date&time format] ;; a ≈ weekday; b ≈ month; d ≈ numeric day, R ≈ 24hr:minute. (setq display-time-format "%a %b %d ╱ %r") ;; E.g.,: Fri Mar 04 ╱ 03:42:08 pm (setq display-time-interval 1) ;; Please update the time every second. (display-time-mode)
But display-time-mode shows me a bit more info that I actually don't care for; so let's disable those.
;; I don't need the system load average in the modeline. (setq display-time-default-load-average nil) (setq display-time-load-average nil)
6.3.4. Column Numbers
Likewise, let's have the modeline display column numbers, but not line numbers. Instead, let's have line numbers on the side of the buffer; moreover let's have a uniform width for displaying line numbers, rather than having the width grow as necessary.
;; (column-number-mode t) ;; Enabled in doom-modeline by default ;; (line-number-mode t) ;; Not sure I want line numbers in modeline, since I have them in the left margin.
Line numbers are a conventionally expected part of a user interface, but I've realised that I seldom need to see them. I can still jump to a line number provided by a compilation error with M-g g; and toggle line numbers on when I'm pair programming with display-line-numbers-mode.
- In Emacs, there are buffer which exist and contain textual data, but to actually see them one requires a window. In the same vein, there are line numbers but I don't need to always see them.
- If I need an indication of ‘progress’, the modeline contains a percentage of how far I am in a buffer.
;; (setq display-line-numbers-width-start t) ;; (global-display-line-numbers-mode t) (global-linum-mode -1)
6.4. Exquisite Fonts and Themes
Emacs' default theme leaves much to be desired: It does not look sleek and shiny, which usually leaves first-timers with a poor, shallow, impression of the system. Below we install a few themes that make Emacs look exquisite. We cycle between the chosen themes with C-c t, my/toggle-theme.
“my/toggle-theme” Implementation
M-x load-theme RET TAB
shows all themes, including built-in ones, that may be loaded.- Loading multiple themes results in their pallets mixed.
M-x disable-theme
to remove a theme from the current pallet.
;; Treat all themes as safe; no query before use. (setf custom-safe-themes t) ;; Nice looking themes ^_^ (use-package solarized-theme :defer t) (use-package doom-themes :defer t) (use-package spacemacs-common :defer t :ensure spacemacs-theme)
- The Doom Themes also look rather appealing.
- A showcase of many themes can be found here.
;; Infinite list of my commonly used themes. (setq my/themes '(doom-laserwave doom-solarized-light doom-vibrant spacemacs-light solarized-gruvbox-dark solarized-gruvbox-light)) (setcdr (last my/themes) my/themes)
C-c t to toggle between the personal themes.
(cl-defun my/disable-all-themes (&optional (new-theme (pop my/themes))) "Disable all themes and load NEW-THEME, which defaults from ‘my/themes’. When a universal prefix is given, “C-u C-c t”, we load a random theme from all possible themes. Nice way to learn about more themes (•̀ᴗ•́)و" (interactive) (mapc #'disable-theme custom-enabled-themes) (-let [theme (if current-prefix-arg (nth (random (length (custom-available-themes))) (custom-available-themes)) new-theme)] (when theme (load-theme theme) (message "Theme %s" theme)))) (defalias 'my/toggle-theme #' my/disable-all-themes) (global-set-key "\C-c\ t" 'my/toggle-theme) ;; (my/toggle-theme) (use-package solarized-theme) (my/toggle-theme 'doom-laserwave)
Apparently, there's already a package that accomplishes these goals and more: theme-looper. I may switch to it, but for now my simple function above is slightly informative, to me at least, about how themes work and it does what I want.
…Actually, the above learning adventure has made it easy to provide a similar setup for fonts 😁
Likewise, C-c F, my/toggle-font, to quickly change fonts (according to mood 😸). [I already use C-c f, my/org-mode-format, for the more likely operation of formatting text.]
“my/toggle-font” Implementation
;; Infinite list of my commonly used fonts (setq my/fonts '("Roboto Mono Light 14" ;; Sleek "Input Mono 14" "Source Code Pro Light 14" ;; thin, similar to Inconsolata Light "Papyrus 14" "Bradley Hand Light 12" ;; "Chalkduster 14" ;; Laggy! "Courier Light 12" "Noteworthy 9" "Savoye LET 14" "Fantasque Sans Mono 16" )) (setcdr (last my/fonts) my/fonts) ;; Let's ensure they're on our system ;; brew search "/font-/" # List all fonts (shell-command "brew tap homebrew/cask-fonts") (system-packages-ensure "svn") ;; Required for the following font installs (system-packages-ensure "font-roboto-mono") (system-packages-ensure "font-input") (system-packages-ensure "font-source-code-pro") (system-packages-ensure "font-fira-mono") (system-packages-ensure "font-mononoki") (system-packages-ensure "font-monoid") (system-packages-ensure "font-menlo-for-powerline") (system-packages-ensure "font-fantasque-sans-mono") ;; Use “M-x set-face-font RET default RET”, or... ;; (set-face-font 'default "Source Code Pro Light14") ;; See ~2232 fonts ;; (append (fontset-list) (x-list-fonts "*" nil)) (cl-defun my/toggle-font (&optional (new-font (pop my/fonts))) "Load NEW-FONT, which defaults from ‘my/fonts’. When a universal prefix is given, “C-u C-c F”, we load a random font from all possible themes. Nice way to learn about more fonts (•̀ᴗ•́)و" (interactive) (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil))) (font (if current-prefix-arg (nth (random (length all-fonts)) all-fonts) new-font))) (set-face-font 'default font) (message "Font: %s" font))) (global-set-key "\C-c\ F" 'my/toggle-font) ;; Default font; the “ignore-⋯” is for users who may not have the font. (ignore-errors (my/toggle-font "Fantasque Sans Mono 12")) (ignore-errors (my/toggle-font "Source Code Pro Light 14"))
In any Org file, type elisp:menu-set-font
; then you can click on this link to
get a nice font selection menu —this can be useful for your own ‘personal startup buffer’.
Let's use the following theme and font, upon startup.
(unless noninteractive (my/toggle-font "Roboto Mono Light 14") (my/toggle-theme 'solarized-gruvbox-light))
6.5. Never lose the cursor
Let's have the entire line containing the cursour be slightly highlighted.
;; Make it very easy to see the line with the cursor. (global-hl-line-mode t)
Moreover, we reduce the mental strain of locating the cursour when navigation happens: When we switch windows or scroll, for example, we get a wave of light near the cursor.
(use-package beacon :diminish :config (setq beacon-color "#666600") :hook ((org-mode text-mode) . beacon-mode))
6.6. Dimming Unused Windows
Let's dim windows, and even the whole Emacs frame, when not in use.
(use-package dimmer :config (dimmer-mode))
A more ‘fine-grained’ tool dims all text except the ‘paragraph’ you're working on. It's nifty, but not for me.
6.7. Flashing when something goes wrong
Enable flashing mode-line on errors. E.g., C-g
, or calling an unbound key
sequence, or misspelling a word.
;; (setq visible-bell 1) ;; On MacOS, this shows a caution symbol ^_^ ;; The doom themes package comes with a function to make the mode line flash on error. (use-package doom-themes) (require 'doom-themes-ext-visual-bell) (doom-themes-visual-bell-config)
A blinking cursor rushes me to type; let's slow down. Recently I'm thinking that
a blinking cursours prompts me to continue upwards and onwards.
(blink-cursor-mode 1)
6.9. Highlight & complete parenthesis pair when cursor is near ;-)
Highlight matching ‘parenthesis’ when near one of them.
(setq show-paren-delay 0) (setq show-paren-style 'mixed) (show-paren-mode)
Colour parens, and other delimiters, depending on their depth. Very useful for parens heavy languages like Lisp.
(use-package rainbow-delimiters :disabled :hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode))
For example:
(blue (purple (forest (green (yellow (blue))))))
There is a powerful package called ‘smartparens’ for working with pair-able
characters, but I've found it to be too much for my uses. Instead I'll utilise
the lightweight package electric
, which Emacs provides out of the box.
(electric-pair-mode 1)
It supports, by default, ACSII pairs {}, [], ()
and Unicode ‘’, “”, ⟪⟫, ⟨⟩
.
When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed and considered as pairs. Let's disassociate them from both notions.
;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition. (setq electric-pair-inhibit-predicate (lambda (c) (or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c)))) ;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’. (modify-syntax-entry ?< "w<") (modify-syntax-entry ?> "w>")
Adding Org-emphasise markers for pair completion —Disabled.
Let's add the org-emphasises markers: If we select a word then press *
, it
becomes bold; likewise for /
for emphasise.
(setq electric-pair-pairs '((?~ . ?~) (?* . ?*) (?/ . ?/))) ;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file. ;; Disable pairs when entering minibuffer (add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0))) ;; Renable pairs when existing minibuffer (add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1)))
I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous Org headers, for which the ‘completed closing pair’ must tiresomely be deleted.
6.10. Proportional fonts for Headlines
Let's have headings stick out a bit.
- The larger headings are cute and reminicint of word processors, but having headings coloured is enough —the larger size is too much.
(set-face-attribute 'org-document-title nil :height 2.0) ;; (set-face-attribute 'org-level-1 nil :height 1.0) ;; Remaining org-level-𝒾 have default height 1.0, for 𝒾 : 1..8. ;; ;; E.g., reset org-level-1 to default. ;; (custom-set-faces '(org-level-1 nil))
Remember you can always use Emacs' Custom utility to get Lisp incantations ;-) —See notes on Custom above.
6.11. Making Block Delimiters Less Intrusive
Let us render Org-mode's #+begin_src
and #+end_src
less obtrusively by,
e.g., having the former render as a pencil marker ✎
and the latter as a
tombstone □
—reminiscent of Halmos' QED end-of-proof marker.
Rasmus’ Incantation
This is from Rasmus Roulund.
(defvar-local rasmus/org-at-src-begin -1 "Variable that holds whether last position was a ") (defvar rasmus/ob-header-symbol ?☰ "Symbol used for babel headers") (defun rasmus/org-prettify-src--update () (let ((case-fold-search t) (re "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*") found) (save-excursion (goto-char (point-min)) (while (re-search-forward re nil t) (goto-char (match-end 0)) (let ((args (org-trim (buffer-substring-no-properties (point) (line-end-position))))) (when (org-string-nw-p args) (let ((new-cell (cons args rasmus/ob-header-symbol))) (cl-pushnew new-cell prettify-symbols-alist :test #'equal) (cl-pushnew new-cell found :test #'equal))))) (setq prettify-symbols-alist (cl-set-difference prettify-symbols-alist (cl-set-difference (cl-remove-if-not (lambda (elm) (eq (cdr elm) rasmus/ob-header-symbol)) prettify-symbols-alist) found :test #'equal))) ;; Clean up old font-lock-keywords. (font-lock-remove-keywords nil prettify-symbols--keywords) (setq prettify-symbols--keywords (prettify-symbols--make-keywords)) (font-lock-add-keywords nil prettify-symbols--keywords) (while (re-search-forward re nil t) (font-lock-flush (line-beginning-position) (line-end-position)))))) (defun rasmus/org-prettify-src () "Hide src options via `prettify-symbols-mode'. `prettify-symbols-mode' is used because it has uncollpasing. It's may not be efficient." (let* ((case-fold-search t) (at-src-block (save-excursion (beginning-of-line) (looking-at "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")))) ;; Test if we moved out of a block. (when (or (and rasmus/org-at-src-begin (not at-src-block)) ;; File was just opened. (eq rasmus/org-at-src-begin -1)) (rasmus/org-prettify-src--update)) ;; Remove composition if at line; doesn't work properly. ;; (when at-src-block ;; (with-silent-modifications ;; (remove-text-properties (match-end 0) ;; (1+ (line-end-position)) ;; '(composition)))) (setq rasmus/org-at-src-begin at-src-block))) (defun rasmus/org-prettify-symbols () (mapc (apply-partially 'add-to-list 'prettify-symbols-alist) (cl-reduce 'append (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x)))) `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎ ("#+end_src" . ?□) ;; ⏹ ("#+header:" . ,rasmus/ob-header-symbol) ("#+begin_quote" . ?») ("#+end_quote" . ?«))))) (turn-on-prettify-symbols-mode) (add-hook 'post-command-hook 'rasmus/org-prettify-src t t)) ;; Last updated: 2019-06-09
(add-hook 'org-mode-hook #'rasmus/org-prettify-symbols) (org-mode-restart)
His development relies on built-in prettify-symbols-mode, which
disguises strings in a buffer for the sake of readability or
aesthetics. Following the example in the documentation, C-h f
prettify-symbols-mode
, we can quickly approximate his efforts for
example
blocks as follows, however a main issue is that source blocks
have busybodied headers which his setup disguises as ‘≡’.
(global-prettify-symbols-mode) (defvar my/prettify-alist nil "Musa's personal prettifications.") (cl-loop for pair in '(;; Example of how pairs like this to beautify org block delimiters ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒ ("#+end_example" . ?⇐) ;; ⇐ ;; Actuall beautifications ("==" . ?≈) ("===" . ?≈) ("=" . ?≔) ;; Programming specific prettifications ("<=" . ?≤) (">=" . ?≥) ("->" . ?→) ("-->". ?⟶) ;; threading operators ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols do (push pair my/prettify-alist)) ;; Replace all Org [metadata]keywords with the “▷” symbol; e.g., “#+title: Hello” looks like “▷ Hello”. (cl-loop for keyword in '(title author email date description options property startup export_file_name html_head) do (push (cons (format "#+%s:" keyword) ?▷) my/prettify-alist)) (cl-loop for hk in '(text-mode-hook prog-mode-hook org-mode-hook) do (add-hook hk (lambda () (setq prettify-symbols-alist (append my/prettify-alist prettify-symbols-alist)))))
See “Mathematical Notation in Emacs” for how such prettifications can make verbose (Python) scripts much more readable by employing more economical disguises.
A nice sanity:
;; Un-disguise a symbol when cursour is inside it or at the right-edge of it. (setq prettify-symbols-unprettify-at-point 'right-edge)
6.12. Hiding Emphasise Markers, Inlining Images, and LaTeX-as-PNG
Let's make some things prettier than they appear by default.
;; org-mode math is now highlighted ;-) (setq org-highlight-latex-and-related '(latex)) ;; Extra space between text and underline line (setq x-underline-at-descent-line t) ;; Hide the *,=,/ markers (setq org-hide-emphasis-markers t) ;; Let’s limit the width of images inlined in org buffers to 400px. (setq org-image-actual-width 400) ;; Visually, I prefer to hide the markers of macros, so let’s do that: ;; {{{go(here)}}} is shown in Emacs as go(here) (setq org-hide-macro-markers t) ;; On HTML exports, Org-mode tries to include a validation link for the exported HTML. Let’s disable that since I never use it. ;; (setq org-html-validation-link nil) ;; Musa: This is super annoying, in practice. (setq org-pretty-entities nil) ;; Also makes subscripts (x_{sub script}) and superscripts (x^{super script}) appear in org in a WYSIWYG fashion. ;; to have \alpha, \to and others display as utf8 ;; http://orgmode.org/manual/Special-symbols.html ;; ;; Be default, any consectuive string after “_” or “^” will be shown in WYSIWYG fashion; the following requires “^{⋯}” instead. ;; (setq org-use-sub-superscripts (quote {}))
Org pretty entities seems rather impressive ---M-x org-entities-help
to see all
possibilities, or add your own. I'm already using the Agda input method, so I
wont use Org's —Agda's gives me a tiny menu narrowing possibilities as I type.
However, it does make subscripts (xsub script) and superscripts (xsuper script) appear in Org in a WYSIWYG fashion.
Automatically display emphasis markers and links when the cursor is on them.
(c.f. fragtog
below)
(use-package org-appear :hook (org-mode . org-appear-mode) :init (setq org-appear-autoemphasis t org-appear-autolinks nil org-appear-autosubmarkers nil))
The following is now disabled (yet again, as of Dec/31/2020) —it makes my system slower than I'd like.
;; Show inline images when loading a new Org file. (setq org-startup-with-inline-images t) ;; Whenever a src block is run, redisplay images so they're up-to-date. ;; Very useful when using ‘ob-latex-as-png’, below. (add-hook 'org-babel-after-execute-hook #'org-redisplay-inline-images) ;; Automatically convert LaTeX fragments to inline images. (setq org-startup-with-latex-preview t)
Org mode supports inline image previews of LaTeX fragments; e.g., \(e^{i \cdot \pi} - 1 = 0\) or \(\substack{𝔹 \\ ↓ \\ 𝒜}\). These can be toggled with C-c C-x C-l. Org-fragtog automates this, so fragment previews are disabled for editing when your cursor steps onto them, and re-enabled when the cursor leaves.
;; Automatically toggle LaTeX previews when cursour enters/leaves them (use-package org-fragtog :hook (org-mode . org-fragtog-mode))
org-latex-preview, C-c C-x C-l, renders $e^{i \pi} + 1 = 0$
into a
really nice inline image: \(e^{i \pi} + 1 = 0\). It also works for LaTeX
environments —for personal environments, just (add-to-list
'org-latex-packages-alist "LaTeX definitions here")
.
;; Make previews a bit larger (setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) ;; I use a lot of Unicode, so let's always include a unicode header. (maybe-clone "https://armkeh.github.io/unicode-sty/") (setq org-format-latex-header (concat org-format-latex-header "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}")) ;; ;; Now this looks nice too! ;; $\substack{𝔹 \\ ↓ \\ 𝒜}$ and $\mathbb{B}$. ;; Always support unicode upon LaTeX export ;; No need to explicitly import armkeh's unicode-sty in each org file. (add-to-list 'org-latex-packages-alist "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}")
This approach does not work well for forming diagrams; I've tried to make tikzcd work this way and failed. Using ob-latex-as-png as a substitute.
;; Support “latex-as-png” src blocks, which show LaTeX as PNGs (use-package ob-latex-as-png)
Use ref:my-stuff
to refer to an Org entity with #+name: my-stuff
; which must
have a #+caption: ⋯
as well. Example entities include tables and source
blocks; as well as figure blocks. For equation blocks, you must use a
\label{⋯}
directly.
;; Use the “#+name” the user provides, instead of generating label identifiers. (setq org-latex-prefer-user-labels t)
6.13. Show off-screen heading at the top of the window
In case we forgot which heading we're under, let's keep the current heading stuck at the top of the window.
(use-package org-sticky-header :hook (org-mode . org-sticky-header-mode) :config (setq-default org-sticky-header-full-path 'full ;; Child and parent headings are seperated by a /. org-sticky-header-outline-path-separator " / "))
6.14. Powerful Directory Editing with dired
⟨ C-x C-v
to open a file or directory in dired, using the current buffer. ⟩
As mentioned earlier, dired
is Emacs' built-in directory editor; it's opened
with C-x d
. Dired let's us treat directories as textual objects! In dired,
press h
to see the many actions available. Here's a few…
Super Terse ‘dired’ Tutorial
(
toggles hiding entry details, such as modification date and ownerships
sort entries; modeline will display “Dired by date” or “Dired by name”.o
to open entry in anOther window; orRET
to open in place.+
to create a new directory; orM-x make-directory
./
to filter entries; withwhich-key
, possible completions pop-up.- E.g.,
/ f
shows only files or/ . png
to obtain all entries with extensionpng
. / i g
to hide git-ignored items ^_^/ /
to remove all filters.
- E.g.,
TAB
to navigate between different groupings of entries.RET
on a drawer heading toggles folding it ^_^
The dired-hacks family of packages lets us, say, get a dired buffer out of a shell incantation that lists files, or use dired to open files with external tools. Below we use three of its packages.
Pressing i
inserts a directory's children under it, indented, in the current
buffer. Useful to see what's there.
(use-package dired-subtree :bind (:map dired-mode-map ("i" . dired-subtree-toggle)))
When directory 𝒳
has only one child 𝒴
, then in dired, instead of 𝒳
, show me 𝒳/𝒴
with 𝒳
greyed out.
(use-package dired-collapse :hook (dired-mode . dired-collapse-mode))
Begin dired with certain entries grouped together, according to some filtering
requirement; and with “garbage” files not shown —i.e., those ending in
.aux, .out
, etc.
(use-package dired-filter :hook (dired-mode . (lambda () (dired-filter-group-mode) (dired-filter-by-garbage))) :custom (dired-garbage-files-regexp "\\(?:\\.\\(?:aux\\|bak\\|dvi\\|log\\|orig\\|rej\\|toc\\|out\\)\\)\\'") (dired-filter-group-saved-groups '(("default" ("Org" (extension "org")) ("Executables" (exexutable)) ("Directories" (directory)) ("PDF" (extension "pdf")) ("LaTeX" (extension "tex" "bib")) ("Images" (extension "png")) ("Code" (extension "hs" "agda" "lagda")) ("Archives"(extension "zip" "rar" "gz" "bz2" "tar"))))))
[Disabled] Neotree: Traditional Directory Tree Navigation
We open a nifty file manager upon startup.
;; Sidebar for project file navigation (use-package neotree :defer t :disabled :config (global-set-key "\C-x\ d" 'neotree-toggle) (setq neo-theme 'icons)) ;; Uses all-the-icons from § Booting Up ;; Open it up upon startup. ;; (neotree-toggle)
By default C-x d
invokes dired
, but I prefer neotree
for file
management.
⟨ Edit: As a naive user, this is what I thought; yet a year later, I've almost never used neotree. ⟩
Useful navigational commands include
U
to go up a directory.C-c C-c
to change directory focus;C-C c
to type the directory out.?
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.
6.15. Persistent Scratch Buffer
The *scratch*
buffer is a nice playground for temporary data or experiments.
However, by default its contents are not saved –which may be an issue if we have not relocated our playthings to their appropriate files. Whence let's save & restore the scratch buffer by default.
(use-package persistent-scratch :defer t ;; In this mode, the usual save key saves to the underlying persistent file. :bind (:map persistent-scratch-mode-map ("C-x C-s" . persistent-scratch-save)))
We might accidentally close this buffer, so we could utilise the following.
(defun scratch () "Recreate the scratch buffer, loading any persistent state." (interactive) (switch-to-buffer-other-window (get-buffer-create "*scratch*")) (condition-case nil (persistent-scratch-restore) (insert initial-scratch-message)) (org-mode) (persistent-scratch-mode) (persistent-scratch-autosave-mode 1)) ;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch* ;; Upon startup, close the default scratch buffer and open one as specfied above (ignore-errors (kill-buffer "*scratch*") (scratch))
I use Org-mode often, so that's how I want things to appear.
(setq initial-scratch-message (concat "#+Title: Persistent Scratch Buffer" "\n#\n# Welcome! This’ a place for trying things out." "\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n"))
6.16. Tabs Disabled
I really like my Helm-supported C-x b
, but the visial appeal of a tab bar for Emacs
is interesting. Let's try it out and see how long this lasts —it may be like Neotree:
Something cute to show to others, but not as fast as the keyboard.
(use-package awesome-tab :disabled :quelpa (awesome-tab :fetcher git :url "https://github.com/manateelazycat/awesome-tab.git") :config (awesome-tab-mode t)) ;; Show me /all/ the tabs at once, in one group. (defun awesome-tab-buffer-groups () (list (awesome-tab-get-group-name (current-buffer))))
It's been less than three days and I've found this utility to be unhelpful, to me anyhow.
An alternative is centaur-tabs.
6.17. Window resizing using the golden ratio Disabled
Let's load the following package, which automatically resizes windows so that the window containing the cursor is the largest, according to the golden ratio. Consequently, the window we're working with is nice and large yet the other windows are still readable.
(use-package golden-ratio :disabled :diminish golden-ratio-mode :init (golden-ratio-mode 1))
After some time this got a bit annoying and I'm no longer using this.
6.18. Org-Emphasise for Parts of Words Disabled
From stackoverflow, the following incantation allows us to have parts of works emphasied with org-mode; e.g., /half/ed, ~half~ed, and right in the m*idd*le! Super cool stuff!
(setcar org-emphasis-regexp-components " \t('\"{[:alpha:]") (setcar (nthcdr 1 org-emphasis-regexp-components) "[:alpha:]- \t.,:!?;'\")}\\") (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components)
I've disabled this feature since multiple occurrences of an emphasise marker are sometimes treated as one lengthy phrase being emphasised.
6.19. Preview link under cursor
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 goto-line / 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)
6.20. Replace phrases with nice SVG labels
let's us replase arbitrary regular expressions with beautiful SVG images that can be clicked to produce an action, and may have a tooltip to provide contextual information. Essentially an alternative to the built-in font-lock-mode, which performs arbitrary syntax highlighting.
- For more power, use the
svg-lib
package. - The docs have nice examples. Here are more useful examples.
Below I setup a function, my/svg-tag-declare-badge to declaratively produce SVG badges.
(use-package svg-tag-mode :hook (org-mode prog-mode) ;; :config (global-svg-tag-mode) ;; Nope: Breaks xwidget-webkit-browse-url, issue#28. :config (cl-defun my/svg-tag-declare-badge (template face &optional tooltip-message-upon-hover) ;; Example faces: 'org-level-1 'org-todo 'font-lock-doc-face "Given a TEMPLATE of the shape \"𝑿❙𝒀\", make SVG badge whose tag is 𝑿 and label is 𝒀. When `svg-tags-mode' is enabled, every occurence of \"\\(𝑿\\)\\(𝒀\\)\" is replaced by an SVG image essentially displaying “[𝑿∣𝒀]” using the given FACE. This badge can be clicked to show all instances in the buffer. You can see the badges documentation / intentions / help-message when you hover over it; to see TOOLTIP-MESSAGE-UPON-HOVER. Both 𝑿 and 𝒀 are regeular expressions; “❙” serves as the SVG tag-label delimiter ---i.e., it saves as from writing \"\\(𝑿\\)\\(𝒀\\)\". Moreover, the SVG is only active when regexp \"\\(𝑿\\)\\(𝒀\\)\" matches an instance." ;; Append tooltip message with a notice on what happens upon click. (--> "Click on me to all see occurrences of this badge, in the current buffer!" (if tooltip-message-upon-hover (concat tooltip-message-upon-hover "\n\n" it) it) (setq tooltip-message-upon-hover it)) (-let [(tag label) (s-split "❙" template)] (-let [click-to-show-all-buffer-occurrences `(lambda () (interactive) (occur (concat ,tag ,label)))] ;; Make an SVG for the tag. (push (cons (format "\\(%s\\)%s" tag label) `((lambda (tag) (svg-tag-make (s-chop-suffix ":" (s-chop-prefixes '("[" "<" "/*") tag)) :face (quote ,face) :inverse t :margin 0 :crop-right t :crop-left nil)) ,click-to-show-all-buffer-occurrences ,tooltip-message-upon-hover)) svg-tag-tags) ;; Make an SVG for the label. (push (cons (format "%s\\(%s\\)" tag label) `((lambda (label) (svg-tag-make (s-chop-suffixes '("]" ">" "*/") label) :face (quote ,face) :crop-left t)) ,click-to-show-all-buffer-occurrences ,tooltip-message-upon-hover)) svg-tag-tags)))) ;; Let's start off empty; then declare badges below. (setq svg-tag-tags nil) ;; Using caps so that these stick-out somewhat even when svg-tags-mode is not present. (my/svg-tag-declare-badge "(my/svg-tag-declare-badge "
(my/svg-tag-declare-badge "
(my/svg-tag-declare-badge "
(my/svg-tag-declare-badge "
;; [In]Active Time stamps --- M-x org-time-stamp (my/svg-tag-declare-badge "\\
" '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 "
" '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!") ;;
) ;; If everything is setup, the following examples should look like SVGs. ;;
;;
;;
;;
;;
;;
;;
;;
;;
;;
;; (progn (svg-tag-mode-off) (svg-tag-mode-on)) ;;
![]()
7. Prose
Emacs can be setup with a spellchecker and other expected features of a word processing tool —however these features apply Emacs-wide since nearly everything is essentially text (•̀ᴗ•́)و
- Org-mode is a writer's best friend; it's large enough to deserve its own sections.
7.1. Whitespace
Let's start off by cleaning-up any accidental trailing whitespace and in other places upon save.
(add-hook 'before-save-hook 'whitespace-cleanup)
See here for making whitespace visible; including spaces, tabs, and newlines
7.2. Formatting Text
The following incantation, my/org-mode-format, makes it so that we can select some text then press C-c f (to get a list of possible character completions) then press the symbol we want our text to be surrounded with.
Details
(local-set-key (kbd "C-c f") #'my/org-mode-format) (defun my/org-mode-format (&optional text) "Surround selected region with the given Org emphasises marker. E.g., if this command is bound to “C-c f” then the sequence “C-c f b” would make the currenly selected text be bold. Likewise, “C-c f *” would achieve the same goal. When you press “C-c f”, a message is shown with a list of useful single-character completions. Note: “C-c f 𝓍”, for an unrecognised marker 𝓍, just inserts the character 𝓍 before and after the selected text." (interactive "P") ;; Works on a region ; (message "b,* ⟨Bold⟩; i,/ ⟨Italics⟩; u,_ ⟨Underline⟩; c,~ ⟨Monotype⟩") (message "⟨Bold b,*⟩ ⟨Italics i,/⟩ ⟨Underline u,_⟩ ⟨Monotype c,~⟩") (let ((kind (read-char))) ;; Map letters to Org formatting symbols (setq kind (or (plist-get '(b ?\* i ?\/ u ?\_ c ?\~) (intern (string kind))) kind)) (insert-pair text kind kind)))
7.3. Fill-mode —Word Wrapping
In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting paragraphs. This is a powerful form of “word wrap”.
(setq-default fill-column 80 ;; Let's avoid going over 80 columns truncate-lines nil ;; I never want to scroll horizontally indent-tabs-mode nil) ;; Use spaces instead of tabs
Certain variables are sensibly local to a buffer, and so setq
only alters their
value for one buffer. Using setq-default
we change a variable's default value,
in every buffer.
;; Wrap long lines when editing text (add-hook 'text-mode-hook 'turn-on-auto-fill) (add-hook 'org-mode-hook 'turn-on-auto-fill) ;; Do not show the “Fill” indicator in the mode line. (diminish 'auto-fill-function)
We may press M-q
to cleverly redistribute the line breaks within any paragraph,
thereby making it look better. With a prefix argument, it justifies it as well
—i.e., pads extra white space to make the paragraph appear rectangular.
Note that M-o M-s
centres a line of text ;-) Fun stuff!
Fill-mode is also known as “hard word wrapping”, which has the counterpart “soft word wrapping” …
Visual line mode is built-in and provides support for editing by visual lines:
Lines off the screen are visually word wrapped, but logically remain one line.
Moreover C-a,e,k
operate on visual lines rather than logical lines.
;; Bent arrows at the end and start of long lines. (setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow)) (diminish 'visual-line-mode) (global-visual-line-mode 1)
Visual line mode is useful when I have way too many windows open or when using smaller frames.
7.4. Pretty Lists Markers
When writing, it's common to use +,-,*
to enumerate unordered lists
—especially so in Org-mode wherein they denote structured text. Let's render
them visually as Unicode bullets.
;; (x y z) ≈ (existing-item replacement-item positivity-of-preceding-spaces) (cl-loop for (x y z) in '(("+" "◦" *) ("-" "•" *) ("*" "⋆" +)) do (font-lock-add-keywords 'org-mode `((,(format "^ %s\\([%s]\\) " z x) (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ,y)))))))
7.5. Fix spelling as you type —thesaurus & dictionary too!
I would like to check spelling on the fly.
C-;
- Cycle through corrections for word at point.
M-$
- Check and correct spelling of the word at point
M-x ispell-change-dictionary RET TAB
- To see what dictionaries are available.
Install spell-checking application as well as a reliable English dictionary, WordNet.
(system-packages-ensure "aspell") (system-packages-ensure "wordnet")
flyspell-prog-mode
enables spell checking for programming by only considering
comments and strings.
(use-package flyspell :diminish :hook ((prog-mode . flyspell-prog-mode) ((org-mode text-mode) . flyspell-mode)))
Enabling fly-spell for text-mode enables it for org and latex modes since they derive from text-mode.
Flyspell needs a spell checking tool, which is not included in Emacs. We
install aspell
spell checker using, say, homebrew via brew install aspell
. Note
that Emacs' ispell
is the interface to such a command line spelling utility.
(setq ispell-program-name "/usr/local/bin/aspell") (setq ispell-dictionary "en_GB") ;; set the default dictionary
[Disabled] Allow spelling support for CamlCase words like “EmacsIsCool”.
(setq ispell-extra-args '("--sug-mode=ultra" "--run-together" "--run-together-limit=5" "--run-together-min=2"))
Let us select a correct spelling merely by clicking on a word —for the rare days I have a mouse.
(eval-after-load "flyspell" ' (progn (define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word) (define-key flyspell-mouse-map [mouse-3] #'undefined)))
Colour incorrect works; default is an underline.
(global-font-lock-mode t) (custom-set-faces '(flyspell-incorrect ((t (:inverse-video t)))))
Finally, save to user dictionary without asking:
(setq ispell-silently-savep t)
Let's keep track of my personal word set by having it be in my version controlled
.emacs directory. Note that the default location is ~/.[i|a]spell.DICT
for
a specified dictionary DICT
.
(setq ispell-personal-dictionary "~/.emacs.d/.aspell.en.pws")
Nowadays, I very rarely write non-literate programs, but if I do I'd like to check spelling only in comments/strings. E.g.,
(add-hook 'c-mode-hook 'flyspell-prog-mode) (add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode)
Use the thesaurus Emacs frontend Synosaurus to avoid unwarranted repetition.
(use-package synosaurus :diminish synosaurus-mode :init (synosaurus-mode) :config (setq synosaurus-choose-method 'popup) ;; 'ido is default. (global-set-key (kbd "M-#") 'synosaurus-choose-and-replace))
The thesaurus is powered by the Wordnet wn
tool, which can be invoked without an
internet connection!
;; (shell-command "brew cask install xquartz &") ;; Dependency ;; (shell-command "brew install wordnet &")
Let's use Wordnet as a dictionary via the wordnut package.
(use-package wordnut :bind ("M-!" . wordnut-lookup-current-word)) ;; Use M-& for async shell commands.
Use M-↑,↓
to navigate dictionary results, and wordnut-search
for a new search.
An alternative to wordnut
is to use the lightweight define-word
package; which I
think is not ideal since it provides way less information.
7.6. Using a Grammar & Style Checker
[ A possibly better alternative is Vale. ]
Let's install a grammar and style checker. We get the offline tool from the bottom of the LanguageTool website, then relocate it as follows.
(use-package langtool :defer t :custom (langtool-language-tool-jar "~/Applications/LanguageTool-4.5/languagetool-commandline.jar"))
Now we can run langtool-check
on the subsequent grammatically incorrect
text —which is from the LanguageTool website— which colours errors in red,
when we click on them we get the reason why; then we may invoke
langtool-correct-buffer
to quickly use the suggestions to fix each correction,
and finally invoke langtool-check-done
to stop any remaining red colouring.
LanguageTool offers spell and grammar checking. Just paste your text here and click the 'Check Text' button. Click the colored phrases for details on potential errors. or use this text too see an few of of the problems that LanguageTool can detecd. What do you thinks of grammar checkers? Please not that they are not perfect. Style issues get a blue marker: It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017 --uh oh, that's the wrong date ;-)
By looking around the source code, I can do all three stages smoothly (•̀ᴗ•́)و
;; Quickly check, correct, then clean up /region/ with M-^ (eval-after-load 'langtool (progn (add-hook 'langtool-error-exists-hook (lambda () (langtool-correct-buffer) (langtool-check-done))) (global-set-key "\M-^" (lambda () (interactive) (message "Grammar checking begun ...") (langtool-check)))))
The checking command is silent, we added a bit of comforting acknowledgement to the user.
7.7. Lightweight Prose Proofchecking
Let's write good!
(use-package writegood-mode ;; Load this whenver I'm composing prose. :hook (text-mode org-mode) ;; Don't show me the “Wg” marker in the mode line :diminish ;; Some additional weasel words. :config (--map (push it writegood-weasel-words) '("some" "simple" "simply" "easy" "often" "easily" "probably" "clearly" ;; Is the premise undeniably true? "experience shows" ;; Whose? What kind? How does it do so? "may have" ;; It may also have not! "it turns out that"))) ;; How does it turn out so? ;; ↯ What is the evidence of highighted phrase? ↯
Inspired by Matt Might's 3 shell scripts to improve your writing, or "My Ph.D. advisor rewrote himself in bash", this Emacs interface emphasises, via underline, the following weaknesses in writing —so that I can fix them or decide that they are appropriate for the scenario.
Sentences that cut out the following problems may become stronger —by being more terse or precise.
- Weasel Words
Phrases that sound good without conveying information; such as vague precision or subjective phrases.
E.g., a number of, surprisingly, very close.
It's okay not to have exact details, but rather than “I don't know” explain why not and what the next steps will be.
- Passive Voice
Phrases wherein interest is in the object experiencing an action, rather than the subject that performs the action.
- Bad: The house was built by my father.
- Good: My father built this house.
Likewise, including relevant or explanatory information as in “X guarantees Y” is an improvement over “Y is guaranteed”.
Sometimes the subject really is irrelevant, such as “We did X” whereas “X happened” suffices.
👍 If the relevant subject is unclear and, also, the text reads better in the active, then change a phrase.
- Duplicated Words
Occurrences of, say, “the the”.
Harder to catch manually, but easier mechanically ;-)
7.8. Placeholder Text —For Learning & Experimenting
When learning about Emacs formatting commands, such as zap-to-char M-z
or transpose M-t
, it's best to have filler text —even better when
it's automatically generated instead of typing it out ourselves. The
following will give us a series of commands lorem-ipsum-insert-⋯
for
inserting lists, sentences, paragraphs and using a prefix argument,
with C-u
, we can request to generate any number of them.
(use-package lorem-ipsum :defer t)
‘Lorem’ is not a word itself, but it comes from the Latin ‘Dolorem Ipsum’ which means “pain in and of itself”.
See this Emacs Cheat Sheet to try out the textual navigation and formatting bindings on lorem ipsum, gibberish text.
7.9. Some text to make us smile
The dad-joke queries https://icanhazdadjoke.com to bring us some funny.
(use-package dad-joke :defer t :config (defun dad-joke () (interactive) (insert (dad-joke-get))))
For example, M-x dad-joke
now inserts:
What are the strongest days of the week? Saturday and Sunday…the rest are weekdays.
7.10. Unicode Input via Agda Input
Agda is one of my favourite languages, it's like Haskell on steroids. Let's set it up for the main sake of its Unicode input —you may do likewise using TeX input. ( The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) )
Agda input mode makes it extremely easy to use unicode in documents, something I strongly prefer to do. When I can use symbols directly, instead of (for instance) LaTeX commands, it makes my plaintext far more readable. — Armkeh .emacs config
(system-packages-ensure "agda")
To use the Agda standard library by default
mkdir -p ~/.agda echo /usr/local/lib/agda/standard-library.agda-lib >>~/.agda/libraries echo standard-library >>~/.agda/defaults
Invoke brew info agda
to get these instructions and the version of Agda just
installed.
Get font support for subscripts (, if) need be
- Download and unzip the symbola font
- CMD + SPC ⇒
font book
⇒+
⇒ select the symbola directory you just unzipped
(Note: In the before time, you could brew install this font.)
Executing agda-mode setup
appends the following text to the .emacs
file.
Let's put it here ourselves.
(unless noninteractive (load-file (let ((coding-system-for-read 'utf-8)) (shell-command-to-string "/usr/local/bin/agda-mode locate"))))
I almost always want the agda-mode
input method —it's like the TeX method, but
better.
;; MA: This results in "Package cl is deprecated" !? (unless noninteractive (use-package agda-input :ensure nil ;; I have it locally. :demand t :hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda"))) :custom (default-input-method "Agda"))) ;; Now C-\ or M-x toggle-input-method turn it on and offers ;; TODO add a hook that when the input method becomes Agda, just don't bother showing me in the modeline. ;; E.g., "Π" when using unicode input with Agda ;; Useful to have in the modeline, say when typing in Arabic. ;; (add-variable-watcher ;; 'current-input-method ;; (lambda (_ newvalue 'set _) ;; (setq current-input-method-title ;; (if (equal newvalue "Agda") nil newvalue))))
Unicode doesn't intend to cover things that are achievable with markup, so only a limited subset of the alphabet is available as subscript; but all is available as superscript, except ‘q’.
ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ ᴬ ᴮ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴿ ᵀ ᵁ ⱽ ᵂ ᵅ ᵝ ᵞ ᵟ ᵋ ᶿ ᶥ ᶲ ᵠ ᵡ ᵦ ᵧ ᵨ ᵩ ᵪ
brew cask install font-symbola
⇒ Includes fonts for subscripts; e.g., ₐₙₑₕᵢⱼₖₗₘₙₒₚₜₛ
Below are my personal Agda input symbol translations;
e.g., \set → 𝒮ℯ𝓉
. Note that we could give a symbol new Agda TeX binding
interactively: M-x customize-variable agda-input-user-translations
then
INS
then for key sequence type set
then INS
and for string paste 𝒮ℯ𝓉
.
(unless noninteractive (add-to-list 'agda-input-user-translations '("set" "𝒮ℯ𝓉")))
Better yet, as a loop:
(unless noninteractive (cl-loop for item in '(;; Arabic ornate parenthesis U+FD3E / U+FD3F ("(" "﴾") (")" "﴿") ("cmd" "⌘") ;; categorial ;; ("alg" "𝒜𝓁ℊ") ("split" "▵") ("join" "▿") ("adj" "⊣") (";;" "﹔") (";;" "⨾") (";;" "∘") ;; logic ("if" "⇐") ("onlyif" "⇒") ;; lattices ;; ("meet" "⊓") ("join" "⊔") ;; tortoise brackets, infix relations ("((" "〔") ("))" "〕") ;; residuals ("syq" "╳") ("over" "╱") ("under" "╲") ;; Z-quantification range notation ;; ;; e.g., “∀ x ❙ R • P” ;; ("|" "❙") ("with" "❙") ;; Z relational operators ("domainrestriction" "◁") ("domr" "◁") ("domainantirestriction" "⩤") ("doma" "⩤") ("rangerestriction" "▷") ("ranr" "▷") ("rangeantirestriction" "⩥") ("rana" "⩥") ;; adjunction isomorphism pair ;; ("floor" "⌊⌋") ("lower" "⌊⌋") ("lad" "⌊⌋") ("ceil" "⌈⌉") ("raise" "⌈⌉") ("rad" "⌈⌉") ;; Replies ("yes" "✔") ("no" "❌") ;; Arrows ("<=" "⇐") ;; more (key value) pairs here ) do (add-to-list 'agda-input-user-translations item)))
Also some silly stuff:
(unless noninteractive ;; Add to the list of translations using “emot” and the given, more specfic, name. ;; Whence, \emot shows all possible emotions. (cl-loop for emot in `(;; angry, cry, why-you-no ("whyme" "ლ(ಠ益ಠ)ლ" "ヽ༼ಢ_ಢ༽ノ☂" "щ(゜ロ゜щ)" "‿︵(ಥ﹏ಥ)‿︵" "ಠ_ಠ" "(╬ ಠ益ಠ)" "・゚(*❦ω❦)*・゚" "(╯°□°)╯︵ ┻━┻") ;; flip the table ;; confused, disapprove, dead, shrug, awkward ("what" "「(°ヘ°)" "(ಠ_ಠ)" "(✖╭╮✖)" "¯\\_(ツ)_/¯" "(´°ω°`)" "・✧_✧・") ;; dance, csi ("cool" "┏(-_-)┓┏(-_-)┛┗(-_- )┓" ,(s-collapse-whitespace "•_•) ( •_•)>⌐■-■ (⌐■_■)")) ;; love, pleased, success, yesss, smile, excited, yay ("smile" "♥‿♥" "(─‿‿─)" "(•̀ᴗ•́)و" "ᕦ( ᴼ ڡ ᴼ )ᕤ" "(งಠ_ಠ)ง" "(。◕‿◕。)" "(◕‿◕)" "( ˃ ヮ˂)" "[ ⇀ ‿ ↼ ]" "٩(⁎❛ᴗ❛⁎)۶" "ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ" "(✿◠‿◠)") ;; flower high-5 ("hug" "♡(✿ˇ◡ˇ)人(ˇ◡ˇ✿)♡" "(づ。◕‿◕。)づ" "(づ。◕‿‿‿‿◕。)づ")) do (add-to-list 'agda-input-user-translations emot) (add-to-list 'agda-input-user-translations (cons "emot" (cdr emot)))))
Finally let's effect such translations.
;; activate translations (unless noninteractive (agda-input-setup))
Note that the effect of Emacs unicode input could be approximated using
abbrev-mode
.
7.11. Increase/decrease text size
The ‘usual’ text zoom keys C-±
…
(global-set-key (kbd "C-+") 'text-scale-increase) (global-set-key (kbd "C--") 'text-scale-decrease) ;; C-x C-0 restores the default font size
If thou knowst the ELisp, forgive this shadowing of the negative-argument
… we've still got M--
though.
Curious, this is one of the very first things I did when began using Emacs; yet, perhaps I would not have done it if I was simply told the defaults:
C-x C-=,+
increases text 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.
7.12. Moving Text Around
This extends Org-mode's M-↑,↓
to other modes, such as when coding.
;; M-↑,↓ moves line, or marked region; prefix is how many lines. (use-package move-text :config (move-text-default-bindings))
7.13. Enabling CamelCase Aware Editing Operations
Subword movement lets us treat “EmacsIsAwesome” as three words
─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming
is common among coders. Now, for example, M-f
moves along each subword.
(global-subword-mode 1) (diminish 'subword-mode)
7.14. Delete Selection Mode
Delete Selection mode lets you treat an Emacs region much like a typical text selection outside of Emacs: You can replace the active region. We can delete selected text just by hitting the backspace key.
(delete-selection-mode 1)
7.17. C-c e n,p
: Taking a tour of one's edits
This package allows us to move around the edit points of a buffer without actually undoing anything. We even obtain a brief description of what happend at each edit point. This seems useful for when I get interrupted or lose my train of thought: Just press C-c e p to see what I did recently and where —the “e” is for “e”dit.
;; Give me a description of the change made at a particular stop. (use-package goto-chg :defer t :custom (glc-default-span 0)) (my/defhydra "C-c e" "Look at them edits!" bus :\ ("p" goto-last-change "Goto nᵗʰ last change") ("n" goto-last-change-reverse "Goto more recent change"))
Compare this with C-x u
, or undo-tree-visualise
, wherein undos are actually performed.
Notice, as a hydra, I can use C-c e
followed by any combination of p
and n
to
navigate my recent edits without having to supply the prefix each time.
7.18. visual-regexp
;; While constructing the regexp in the minibuffer, get live visual feedback for the (group) matches. ;; E.g., try: M-% use-\(.+?\) \(.+\)\b ENTER woah \1 and \2 ;; ;; C-u M-% do to regexp replace, without querying. (use-package visual-regexp :config (define-key global-map (kbd "M-%") (lambda (&optional prefix) (interactive "P") (call-interactively (if prefix #'vr/replace #'vr/query-replace)))))
7.19. LaTeX ⇐ Org-Mode
In this section we consider the Org-mode export for PDFs (LaTeX). For example, we account for LaTeX citations.
7.19.1. Get LaTeX:
(system-packages-ensure "mactex")
- This is a redistribution of TeX Live specifically for macOS.
- We get the 4GB version since it has everything and so do not need to worry about missing style files.
- This took about 12 minutes on my machine.
Restart Emacs, enter $e^{i \cdot \pi} + 1 = 0$
then press C-c C-x C-l to
have it rendered inline.
Minted: Get tool for colourful code snippets for LaTeX —see “minted” in the main article.
(system-packages-ensure "pygments")
Not anymore: Get a neato PDF presentation console:
brew install pdfpc
- With
pdfpc myfile.pdf
you get a nice timer and multiple views of the current slide and upcoming slides —with support for multiple monitors. - Install ScreenBrush, from the Apple Store, for easily drawing/annotating my screen —e.g., when I'm giving a virtual lecture to my students.
- An alternative is
brew install mupdf
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
7.19.2. Working with Citations Disabled Not_Used
An exquisite system for handling references.
The following entity will display useful data when the mouse hovers over it (•̀ᴗ•́)و If you click on it, then you're in for a lot of super neat stuff, such as searching for the pdf online!
cite:agdaoverview ( In HTML export, the citation doesn't link anywhere. )
(unless noninteractive (use-package org-ref :custom ;; Files to look at when no “╲bibliography{⋯}” is not present in a file. ;; Most useful for non-LaTeX files. (reftex-default-bibliography '("~/thesis-proposal/papers/References.bib")) (bibtex-completion-bibliography (car reftex-default-bibliography)) (org-ref-default-bibliography reftex-default-bibliography)) ;; Quick BibTeX references, sometimes. (use-package helm-bibtex) (use-package biblio) )
Execute M-x helm-bibtex
or C-c ]
and, say, enter emacs
and you will be
presented with all the entries in the bib database that mention ‘emacs’. Super
cool stuff. Moreover, if no such entries exist, then we can look some up
using the interface!
Read the manual online or better yet as an org-file with M-x org-ref-help
.
This is an Org-mode application since the citations have tooltips and export nicely to LaTeX & HTML via the Org-mode exporter.
7.19.3. Bibliography & Coloured LaTeX using Minted
Execute the following for bibliography references as well as minted Org-mode uses the Minted package for source code highlighting in PDF/LaTeX —which in turn requires the pygmentize system tool.
(setq org-latex-listings 'minted org-latex-packages-alist '(("" "minted")) org-latex-pdf-process '("pdflatex -shell-escape -output-directory %o %f" "biber %b" "pdflatex -shell-escape -output-directory %o %f" "pdflatex -shell-escape -output-directory %o %f"))
For faster pdf generation, possibly with errors, consider invoking:
(setq org-latex-pdf-process '("pdflatex -interaction nonstopmode -output-directory %o %f"))
By default, Org exports LaTeX using the nonstopmode
option, which tries
its best to produce a PDF —which ignores typesetting errors altogether,
which is not necessary ideal when using LaTeX.
7.20. HTML ⇐ Org-mode
In this section we consider the Org-mode exporters for PDFs and HTMLs. For example, we account for LaTeX citations and reliable HTML anchors.
(use-package htmlize :defer t) ;; Main use: Org produced htmls are coloured. ;; Can be used to export a file into a coloured html.
7.20.1. Ensuring Useful HTML Anchors
Upon HTML export, each tree heading is assigned an ID to be used for hyperlinks.
Default IDs are something like org1957a9d
, which does not endure the test of time:
Re-export will produce a different id. Here's a rough snippet to generate
IDs from headings, by replacing spaces with hyphens, for headings without IDs.
(defun my/ensure-headline-ids (&rest _) "Org trees without a All non-alphanumeric characters are cleverly replaced with ‘-’. If multiple trees end-up with the same id property, issue a message and undo any property insertion thus far. E.g., ↯ We'll go on a ∀∃⇅ adventure ↦ We'll-go-on-a-adventure " (interactive) (let ((ids)) (org-map-entries (lambda () (org-with-point-at (point) (let ((id (org-entry-get nil "CUSTOM_ID"))) (unless id (thread-last (nth 4 (org-heading-components)) (s-replace-regexp "[^[:alnum:]']" "-") (s-replace-regexp "-+" "-") (s-chop-prefix "-") (s-chop-suffix "-") (setq id)) (if (not (member id ids)) (push id ids) (message-box "Oh no, a repeated id!\n\n\t%s" id) (undo) (setq quit-flag t)) (org-entry-put nil "CUSTOM_ID" id)))))))) ;; Whenever html & md export happens, ensure we have headline ids. (advice-add 'org-html-export-to-html :before 'my/ensure-headline-ids) (advice-add 'org-md-export-to-markdown :before 'my/ensure-headline-ids)
One may then use [[#my-custom-id]]
to link to the entry with CUSTOM_ID
property my-custom-id
.
Interestingly, org-set-property
, C-c C-x p
, lets us insert a property
from a selection of available ones, then we'll be prompted for a value
for it from a list of values you've used elsewhere. This is useful for
remaining consistent for when trees share similar properties.
7.20.2. Clickable Headlines
By default, HTML export generates ID's to headlines so they may be referenced to, but there is no convenient way to get at them to refer to a particular heading. The following spell fixes this issue: Headlines are now clickable, resulting in a link to the headline itself.
;; Src: https://writepermission.com/org-blogging-clickable-headlines.html (setq org-html-format-headline-function (lambda (todo todo-type priority text tags info) "Format a headline with a link to itself." (let* ((headline (get-text-property 0 :parent text)) (id (or (org-element-property :CUSTOM_ID headline) (ignore-errors (org-export-get-reference headline info)) (org-element-property :ID headline))) (link (if id (format "<a href=\"#%s\">%s</a>" id text) text))) (org-html-format-headline-default-function todo todo-type priority link tags info))))
Warning: The header cannot already be a link! Otherwise you get cyrptic and
unhelpful error (wrong-type-argument plistp :section-number)
; which then
pollutes the current Emacs session resulting in stange nil
errors after C-x C-s
,
thereby forcing a full Emacs restart. Instead, you need at least one portion of
each heading to be not a link.
Need to have a custom id declared.
:PROPERTIES: :CUSTOM_ID: my-header :END:
- Failing headers:
* [[link]]
nor* ~code~
nor* $math$
.- Any non-link text before it will work:
ok [[link]]
.- Using Unicode non-breaking space ‘ ’ is ok.
- Text only after the link is insufficient.
- Any non-link text before it will work:
7.20.3. HTML “Folded Drawers”
(defun my/org-drawer-format (name contents) "Export to HTML the drawers named with prefix ‘fold_’, ignoring case. The resulting drawer is a ‘code-details’ and so appears folded; the user clicks it to see the information therein. Henceforth, these are called ‘fold drawers’. Drawers without such a prefix may be nonetheless exported if their body contains ‘:export: t’ ---this switch does not appear in the output. Thus, we are biased to generally not exporting non-fold drawers. One may suspend export of fold drawers by having ‘:export: nil’ in their body definition. Fold drawers naturally come with a title. Either it is specfied in the drawer body by ‘:title: ⋯’, or otherwise the drawer's name is used with all underscores replaced by spaces. " (let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents)) (fold? (s-prefix? "fold_" name 'ignore-case)) (export? (string-match ":export:\s+t" contents)) (not-export? (string-match ":export:\s+nil" contents)) (title′ (and (string-match ":title:\\(.*\\)\n" contents) (match-string 1 contents)))) ;; Ensure we have a title. (unless title′ (setq title′ (s-join " " (cdr (s-split "_" name))))) ;; Output (cond ((and export? (not fold?)) contents′) (not-export? nil) (fold? (thread-last contents′ (replace-regexp-in-string ":title:.*\n" "") (format "<details class=\"code-details\"> <summary> <strong> <font face=\"Courier\" size=\"3\" color=\"green\"> %s </font> </strong> </summary> %s </details>" title′)))))) (setq org-html-format-drawer-function 'my/org-drawer-format)
With the following invocations we only see the odd indexed ‘hello’s, where the latter two are folded up.
:this-drawer-is-exported: :export: t hello 1 :End: :this-drawer-is-NOT-exported: hello 2 :End: :fold_This_drawer_has_a_title_in_the_body: :title: I am the drawer title 0 hello 3 :End: :fold_This_drawer_is_NOT_exported: :title: Why are we here? :export: nil hello 4 :End: :fold_I_am_the_drawer_title_1: hello 5 :End:
I doubt I could show an example in the Github README, since no HTML export is happening using my setup. In case you're reading this on my blog, which has exported HTML. Here's the example:
Now that I've written this, I'm thinking it may have been preferably to use an org-block…?
7.20.4. Diagrams with Mermaid —Not Reccommended
Let's try out an alternative to PlantUML —covered below in §5.5.
First, let's get the tool.
npm install mermaid.cli
sudo git clone git@github.com:arnm/mermaid-layer.git ~/.emacs.d/private/mermaid
Then, let's get the associated mermaid
package.
(use-package ob-mermaid :custom ob-mermaid-cli-path "~/node_modules/.bin/mmdc")
Then, C-c C-c
on the following:
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.
7.20.5. Reveal.JS – The HTML Presentation Framework
Org-mode documents can be transformed into beautiful slide decks with org-reveal with the following two simple lines.
(use-package ox-reveal :custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"))
For example, execute, C-x C-e
after the closing parenthesis of, the
following block to see an example slide-deck (─‿‿─)
(progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org") (switch-to-buffer (find-file "Trying_out_reveal.org")) (org-reveal-export-to-html-and-browse))
Org-mode exporting, C-c C-e
, now includes an option R
for such reveal slide decks.
Two dimensional slides may be a bit new to some people, so I like to
give viewers an option, in tiny font, to view the slide-deck
continuously and remind them that ?
provides useful shortcuts.
(setq org-reveal-title-slide "<h1>%t</h1> <h3>%a</h3> <font size=\"1\"> <a href=\"?print-pdf&showNotes=true\"> ⟪ Flattened View ; Press <code>?</code> for Help ⟫ </a> </font>")
One should remove the &showNotes=true
if they do not want to include
speaker notes in the flattened view.
Within the flatenned view, one may wish to CTRL/CMD+P
then save the
resulting PDF locally.
7.20.6. Org-mode ⇐ HTML Disabled
The following let's us copy htlm into org format using eww, Emacs' built-in web browser.
;; See: https://emacs.stackexchange.com/questions/7171/paste-html-into-org-mode (use-package org-eww :quelpa (org-eww :fetcher git :url "https://github.com/Fuco1/org-mode.git"))
It does not work as I'd like, but may prove useful to have around.
- Possibly useful: Open a webpage with
M-x eww
then 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 and org-web-tools-convert-links-to-page-entries.
;; C-u C-c C-l ⇒ Paste URL with title, WITHOUT prompting me for anything. ;; C-c C-l ⇒ Prompt me for title. (bind-key* "C-c C-l" (lambda () (interactive) (call-interactively (if current-prefix-arg #'org-web-tools-insert-link-for-url #'my/org-insert-link-dwim)))) ;; From: (defun my/org-insert-link-dwim () "Like `org-insert-link' but with personal dwim preferences. - When text is selected, use that as the link description --and prompt for link type - When a URL is in the clipboard, use that as the link type - On an existing Org link, prompt to alter the link then to alter the description - With a ‘C-u’ prefix, prompts for a file to link to. - It is relative to the current directory; use ‘C-u C-u’ to get an absolute path. It fallsback to `org-insert-link' when possible. Functin Source: https://xenodium.com/emacs-dwim-do-what-i-mean/" (interactive) (let* ((point-in-link (org-in-regexp org-link-any-re 1)) (clipboard-url (when (string-match-p "^http" (current-kill 0)) (current-kill 0))) (region-content (when (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end))))) (cond ((and region-content clipboard-url (not point-in-link)) (delete-region (region-beginning) (region-end)) (insert (org-make-link-string clipboard-url region-content))) ((and clipboard-url (not point-in-link)) (insert (org-make-link-string clipboard-url (read-string "title: " (with-current-buffer (url-retrieve-synchronously clipboard-url) (dom-text (car (dom-by-tag (libxml-parse-html-region (point-min) (point-max)) 'title)))))))) (t (call-interactively 'org-insert-link)))))
8. Programming
Herein we configure utilites for version control, function and variable lookup, and template expansion for inescapably repetitive scenarios.
TODO: Fix these docs
8.1. Quickly Run Code Snippets
Sometimes we want to quickly run some code without making a dedicated file or
with a file but without remembering the terminal incantation to do so, enter
quickrun
. Anywhere, we can select a snippet of code and run quickrun-region
to execute that snippet after selecting the associated programming language, or
quickrun-replace-region if we want the results in-line. If our language of
choice does not exist, we can easily add support for it.
;; In any programming buffer, “M-x quickrun” to execute that program. ;; Super useful when wanting to quickly test things out, in a playground. ;; ;; E.g., Make a new file named “hello.py” containing “print "hi"”, then “M-x quickrun”. ;; ;; Enable “quickrun-autorun-mode” to run code after every save. (use-package quickrun ;; ⇒ “C-c C-r” to see output, “q” to close output ;; ⇒ “C-u C-c C-r” prompts for a language (Useful when testing snippets different from current programming mode) ;; ⇒ In a non-programming buffer, “C-c C-r” runs selected region. :config (bind-key* "C-c C-r" (lambda (&optional start end) (interactive "r") (if (use-region-p) (quickrun-region start end) (quickrun current-prefix-arg)))))
Example…
(system-packages-ensure "rust") ;; Rust Compiler ;; Select the following then press C-c C-r: fn main() { println!("Hello, World!"); }
Actually, let's get a full Rust development environment for Emacs (which also has great support for Org-babel.)
(use-package rustic) ;; Open any Rust file, and run “M-x lsp” which will then prompt you to install ;; rust-analyzer, the rust LSP. ;; ;; LSP for Rust ⇒ Goto definition (M-. / ⌘-l), code completion with types and ;; docstrings, colourful documentation on hover, “Run [Test] | Debug” overlays, ;; super nice stuff! Run “M-!”/[M-x company-show-doc-buffer] if you want the doc in a colourful buffer. ;; ;; Below, hover over “Vec” and see nice, scrollable, colourful docs on vectors. ;; let v:Vec<_> = vec![1, 2, 3]; ;; The offical Rust toolchain installer (system-packages-ensure "rustup") (shell-command "rustup update")
8.2. C-x C-e ∷ REPL-driven development for ELisp / NodeJS / Python / Etc !
Evaluate code and see the results inline —A feedback loop that is faster than ever! |
Within Emacs, C-x C-e evaluates a Lisp expression anywhere; e.g.,
at the end of (message-box "hello world")
press C-x C-e
to see a greeting.
- We ran some code without explicitly running an interpreter/repl/compiler!
- This is known as “REPL driven development” (RDD):
There is a running REPL server for your language, implicitly in the
background, and your editor (say with
C-x C-e
) will send it a line (or a selected region) of code for evaluation; we then see the result as an overlay in our current buffer.- You choose which code gets (re)evaluated.
- A quick introduction to RDD can be viewed at PurelyFunctional.tv.
8.2.1. ELisp
By default, Emacs Lisp's C-x C-e shows results only in the minibuffer; near the bottom of the screen. Let's also have evaluation results displayed as inline overlays —at the location that the user, us, is actually looking/working; rather than forcing their eyes to shift up&down when writing&evaluating.
- C-u C-x C-e inserts the evaluation result at point; C-u 0 C-x C-e does so without truncating lengthy output.
- Read this Sweet & short blog/GIFs on practical uses of C-x C-e when working with Lisp.
;; Evaluation Result OverlayS for Emacs Lisp (use-package eros :init (eros-mode t))
8.2.2. JavaScript
Let's setup RDD for JavaScript —by having a NodeJS repl server running in the background.
- See skerrick: REPL-driven development for Javascript for animated GIFs; it works with modules as well.
(use-package skerrick :init ;; Needs to be run on the very first install of skerrick. Or when you want to upgrade. (unless (equal (shell-command-to-string "type skerrick") "skerrick not found\n") (skerrick-install-or-upgrade-server-binary))) ;; Should be run in a JS buffer; it is buffer specific. ;; (skerrick-start-server) ;; Now main function, entry point is: ;; M-x skerrick-eval-region
Let's provide a quick keyboard shortcut. E.g., C-x C-e evaluates ELisp, so let's mimic that for JS buffers:
(require 'js) ;; Defines js-mode-map ;; Evaluate a region, if any is selected; otherwise evaluate the current line. (bind-key "C-x C-e" (lambda () (interactive) (if (use-region-p) (skerrick-eval-region) (beginning-of-line) (set-mark-command nil) (end-of-line) (skerrick-eval-region) (pop-mark))) 'js-mode-map)
For instance,
// Start the server... then // On each line and press C-x C-e let a = "hello" let b = "world" (a + ' ' + b).toUpperCase() // The final line should show: HELLO WORLD
- Preserving the context
When testing an application, you might notice a bug in a particular context —i.e., a particular configuration in the app.
- The classic approach is to kill the app; i.e., stop the server that is, well, serving the app.
- Solve the problem.
- Try to get back to the configuration, context, you were in beforehand and check that the problem has been resolved.
A better approach is to ignore the bookkeeping steps, 1&3, and just do step 2. For that, there are numerous packages:
- livenode: Live-code your NodeJS applications ⨾⨾ Video demo ~11min ⨾⨾ Last updated 2013
- skewer-mode: Live web development in Emacs ⨾⨾ Silent video demo ~5min ⨾⨾ Last updated 2020
- swank-js: Swank backend for Node.JS and in-browser JavaScript ⨾⨾ Last updated 2015
8.2.3. Python, etc
It seems to be super easy to add such a support for other languages, such as Python. See the final comment here for the tiny change required.
8.3. devdocs
;; 1. Get docs of a languages: M-x devdocs-install ;; 2. Lookup docs: [C-u] M-x devdocs-lookup ;; 𝟚. Lookup docs: [C-u] C-c d (use-package devdocs :bind ("C-c d" . #'devdocs-lookup) :config (when nil ;; “C-x C-e” the following once. (cl-loop for lang in '(javascript ramda typescript html css sass vue~3 vuex~4 vue_router~4 "angularjs~1.6" nginx webpack~5 web_extensions ;; eslint jest jq jsdoc prettier mocha chai jasmine ;; bash docker~19 git homebrew elisp ;; postgresql~14 redis sqlite ;; rust ruby~3 minitest "rails~7.0") do (devdocs-install (list (cons 'slug (format "%s" lang)))))))
8.4. How do I do something?
When programming, sometimes you just gotta Google “how do I do ⋯”.
- The usual process is (1) open a browser, (2) make a Google query, (3) look at StackOverflow's most upvoted answer for your query, (4) copy-paste the code solution/example to your editor; [(5) get distracted by interesting things you'd like to read].
- Better would be to use the howdoi tool, which gives instant coding answers for common questions via the command line.
Below, my Emacs Lisp function howdoi let's me reduce the 4-step process to just 2 steps: Write your query anywhere then call
M-x howdoi
on it to replace the query with the answer. (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)))))
8.5. Sleek Semantic Selection
Super sleek way to select regions: Anywhere press ⌘-r to select the current word, press it again to select sentence, then again for the current paragraph, then more to get the current section.
(use-package expand-region :bind (("s-r" . #'er/expand-region)))
You can watch an introductory ~3 minute video to expand-region at Emacs Rocks!.
That is, repeated ⌘+r expands the selection to the next logical segment of text: In writing this means “Word, sentence, paragraph”, and in programming this means “identifier, then incrementally larger scopes”.
8.6. Managing Processes/Servers from within Emacs —Work-specific functions
Let's make a few interactive Emacs Lisp functions to reduce the amount of time I
need to be in a terminal. I'll use the prefix “w-”
for work stuff. Example
tasks:
- Start/stop my servers
- Interactively select an app to be opened in the browser
- Do database migrations/rollbacks
⟨ Obfuscated with lorem ipsum text. ⟩
Not using this, a bit too verbose to setup for each service but, more accurately, does not Just Workᵀᴹ for my needs.
;; “M-x prodigy”, then press “s” to start a service; “S” to stop it; “$” to see it; “r”estart (use-package prodigy :disabled t) ;; C-h v prodigy-services ⇒ See possible properties.
8.6.1. my/defaliases
(defalias 'defaliases 'my/defaliases) (defmacro my/defaliases (src &rest tgts) "Provide names TGTS as synonymous aliases for SRC, for discovarability. Often a function SRC can be construed from different perspectives, names, purposes TGTS. Another example is when I define things with the ‘my/’ prefix, but also want to use them without. Example use: (my/defaliases view-hello-file greet-others learn-about-the-world) In particular: (my/defaliases OLD NEW) ≈ (defalias 'NEW 'OLD)." `(--map (eval (quote (defalias `,it (quote ,src)))) (quote ,tgts)))
8.6.2. Making unkillable buffers & shells
Making unkillable buffers & shells
(defun my/declare-unkillable-buffer (name) (add-hook 'kill-buffer-query-functions `(lambda () (or (not (equal (buffer-name) ,name)) (progn (message "Not allowed to kill %s, burying instead; otherwise use “M-x force-kill”" (buffer-name)) (bury-buffer)))))) (my/defaliases my/force-kill force-kill w-force-kill) (cl-defun my/force-kill (&optional buffer-name) (interactive) (-let [kill-buffer-query-functions nil] (if buffer-name (kill-buffer buffer-name) (kill-current-buffer)) (ignore-errors (delete-window)))) (cl-defun my/run-unkillable-shell (command &optional (buffer-name command)) "Example use: (my/run-unkillable-shell \"cd ~/my-noejds-project; npm run dev\" \"my-nodejs-project\")" (-let [it (