Beckwith Tangled Emacs Initialization
Table of Contents
- 1. Introduction
- 2. Preamble
- 3. Settings
- 3.1. Keys
- 3.2. Command Launchers
- 3.3. Expansion & Completion
- 3.4. Built-in Features
- 3.5. Minibuffer
- 3.6. Movement
- 3.7. Custom Helpers
- 3.7.1. Auto-display agenda
- 3.7.2. Auto-indent when pasting
- 3.7.3. Better window splitting functions
- 3.7.4. Hide mode line
- 3.7.5. Open/Edit This file
- 3.7.6. Org-column resizing
- 3.7.7. Prettify macro
- 3.7.8. Styled HTML Export
- 3.7.9. Transparency
- 3.7.10. Weekly Score Goal in Org-Agenda
- 3.7.11. Weekly Time Reporting
- 3.7.12. Workweeks
- 3.8. Editing
- 3.9. Images
- 4. Minor Modes
- 5. Major Modes
- 6. Style
- 7. Postamble
1. Introduction
This document tangles (in literate programming style) the necessary commands to initialize Emacs to my liking and the documentation for my choices.
To clone, go to the github repository. For a pretty view, head over to the generated page.
1.1. Installation
My init.el
file is quite simple and is generated by the following
block. Essentially, I just have to install this package
(bnb-emacs
) in the ~/.emacs.d/ directory and run the following
code block (C-c C-c
) to bootstrap the system.
;;; init.el --- bnbeckwith config -*- eval: (read-only-mode 1) -*- ;; START ELPACA INSTALLER (defvar elpaca-installer-version 0.7) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (< emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory \".\" 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (load "./elpaca-autoloads"))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) ;; END ELPACA INSTALLER ;; Install use-package support (elpaca elpaca-use-package ;; Enable :elpaca use-package keyword. (elpaca-use-package-mode) ;; Assume :elpaca t unless otherwise specified. (setq elpaca-use-package-by-default t)) (elpaca-wait) (load "~/.emacs.d/full.el")
From there on, the bootstrapping is simple. Emacs finds ~/.emacs.d/init.el and runs the code.
First, I disable package
, and use the standard elpaca
initialization. Following that, I enable elpaca-use-package
so that
the rest of this setup can use the standard use-package
Declare an Emacs package by specifying a group of configuration options.
For the full documentation, see Info node ‘(use-package) top’.
Usage:
(use-package package-name
[:keyword [option]]...)
:init Code to run before PACKAGE-NAME has been loaded.
:config Code to run after PACKAGE-NAME has been loaded. Note that
if loading is deferred for any reason, this code does not
execute until the lazy load has occurred.
:preface Code to be run before everything except ‘:disabled’; this
can be used to define functions for use in ‘:if’, or that
should be seen by the byte-compiler.
:mode Form to be added to ‘auto-mode-alist’.
:magic Form to be added to ‘magic-mode-alist’.
:magic-fallback Form to be added to ‘magic-fallback-mode-alist’.
:interpreter Form to be added to ‘interpreter-mode-alist’.
:commands Define autoloads for commands that will be defined by the
package. This is useful if the package is being lazily
loaded, and you wish to conditionally call functions in your
‘:init’ block that are defined in the package.
:autoload Similar to :commands, but it for no-interactive one.
:hook Specify hook(s) to attach this package to.
:bind Bind keys, and define autoloads for the bound commands.
:bind* Bind keys, and define autoloads for the bound commands,
*overriding all minor mode bindings*.
:bind-keymap Bind a key prefix to an auto-loaded keymap defined in the
package. This is like ‘:bind’, but for keymaps.
:bind-keymap* Like ‘:bind-keymap’, but overrides all minor mode bindings
:defer Defer loading of a package -- this is implied when using
‘:commands’, ‘:bind’, ‘:bind*’, ‘:mode’, ‘:magic’, ‘:hook’,
‘:magic-fallback’, or ‘:interpreter’. This can be an integer,
to force loading after N seconds of idle time, if the package
has not already been loaded.
:demand Prevent the automatic deferred loading introduced by constructs
such as ‘:bind’ (see ‘:defer’ for the complete list).
:after Delay the effect of the use-package declaration
until after the named libraries have loaded.
Before they have been loaded, no other keyword
has any effect at all, and once they have been
loaded it is as if ‘:after’ was not specified.
:if EXPR Initialize and load only if EXPR evaluates to a non-nil value.
:disabled The package is ignored completely if this keyword is present.
:defines Declare certain variables to silence the byte-compiler.
:functions Declare certain functions to silence the byte-compiler.
:load-path Add to the ‘load-path’ before attempting to load the package.
:diminish Support for diminish.el (if installed).
:delight Support for delight.el (if installed).
:custom Call ‘Custom-set’ or ‘set-default’ with each variable
definition without modifying the Emacs ‘custom-file’.
(compare with ‘custom-set-variables’).
:custom-face Call ‘custom-set-faces’ with each face definition.
:ensure Loads the package using package.el if necessary.
:pin Pin the package to an archive.
(fn NAME &rest ARGS) interface.
Finally, the output of this file, ~/.emacs.d/full.el, is loaded for the complete initialization.
If you are reading this online, the html version of this file is generated by using `bnb/export-readme` explained in Styled HTML Export.
1.2. Notes
This section has specific notes that are relevant to my emacs setup in general and this document in particular.
1.2.1. Elpaca
A newer package manager, Elpaca seeks to be a preferred drop-in
replacement for the built-in package.el
manager. For the
configuration above, I use the standard installation boilerplate
and enable use-package
Declare an Emacs package by specifying a group of configuration options.
For the full documentation, see Info node ‘(use-package) top’.
Usage:
(use-package package-name
[:keyword [option]]...)
:init Code to run before PACKAGE-NAME has been loaded.
:config Code to run after PACKAGE-NAME has been loaded. Note that
if loading is deferred for any reason, this code does not
execute until the lazy load has occurred.
:preface Code to be run before everything except ‘:disabled’; this
can be used to define functions for use in ‘:if’, or that
should be seen by the byte-compiler.
:mode Form to be added to ‘auto-mode-alist’.
:magic Form to be added to ‘magic-mode-alist’.
:magic-fallback Form to be added to ‘magic-fallback-mode-alist’.
:interpreter Form to be added to ‘interpreter-mode-alist’.
:commands Define autoloads for commands that will be defined by the
package. This is useful if the package is being lazily
loaded, and you wish to conditionally call functions in your
‘:init’ block that are defined in the package.
:autoload Similar to :commands, but it for no-interactive one.
:hook Specify hook(s) to attach this package to.
:bind Bind keys, and define autoloads for the bound commands.
:bind* Bind keys, and define autoloads for the bound commands,
*overriding all minor mode bindings*.
:bind-keymap Bind a key prefix to an auto-loaded keymap defined in the
package. This is like ‘:bind’, but for keymaps.
:bind-keymap* Like ‘:bind-keymap’, but overrides all minor mode bindings
:defer Defer loading of a package -- this is implied when using
‘:commands’, ‘:bind’, ‘:bind*’, ‘:mode’, ‘:magic’, ‘:hook’,
‘:magic-fallback’, or ‘:interpreter’. This can be an integer,
to force loading after N seconds of idle time, if the package
has not already been loaded.
:demand Prevent the automatic deferred loading introduced by constructs
such as ‘:bind’ (see ‘:defer’ for the complete list).
:after Delay the effect of the use-package declaration
until after the named libraries have loaded.
Before they have been loaded, no other keyword
has any effect at all, and once they have been
loaded it is as if ‘:after’ was not specified.
:if EXPR Initialize and load only if EXPR evaluates to a non-nil value.
:disabled The package is ignored completely if this keyword is present.
:defines Declare certain variables to silence the byte-compiler.
:functions Declare certain functions to silence the byte-compiler.
:load-path Add to the ‘load-path’ before attempting to load the package.
:diminish Support for diminish.el (if installed).
:delight Support for delight.el (if installed).
:custom Call ‘Custom-set’ or ‘set-default’ with each variable
definition without modifying the Emacs ‘custom-file’.
(compare with ‘custom-set-variables’).
:custom-face Call ‘custom-set-faces’ with each face definition.
:ensure Loads the package using package.el if necessary.
:pin Pin the package to an archive.
(fn NAME &rest ARGS) support.
This is the quick cheat sheet of commands:
Operation | UI (keys apply in elpaca-ui-mode) | completing-read interface commands |
---|---|---|
Finding Packages | M-x elpaca-manager |
elpaca-try Try ORDER.Install the repo/build files on disk. Activate the corresponding package for the current session. ORDER’s package is not made available during subsequent sessions. When INTERACTIVE is non-nil, immediately process ORDER, otherwise queue ORDER. (fn ORDER &optional INTERACTIVE) |
Trying Packages (for current session) | i x |
elpaca-try Try ORDER.Install the repo/build files on disk. Activate the corresponding package for the current session. ORDER’s package is not made available during subsequent sessions. When INTERACTIVE is non-nil, immediately process ORDER, otherwise queue ORDER. (fn ORDER &optional INTERACTIVE) |
Fetching Package Updates | f x |
elpaca-fetch Fetch ID’s associated package remote commits.This does not merge changes or rebuild the packages. If INTERACTIVE is non-nil immediately process, otherwise queue. (fn ID &optional INTERACTIVE) or elpaca-fetch-all Fetch queued elpaca remotes. If INTERACTIVE is non-nil, process queues.(fn &optional INTERACTIVE) |
Merging Updates | u x |
elpaca-merge Merge package commits associated with ID.If FETCH is non-nil, download package changes before merging. If INTERACTIVE is non-nil, the queued order is processed immediately. (fn ID &optional FETCH INTERACTIVE) or elpaca-merge-all Merge and rebuild queued packages.If FETCH is non-nil fetch updates first. If INTERACTIVE is non-nil, process queues. (fn &optional FETCH INTERACTIVE) |
Pulling Updates* | C-u u x |
C-u M-x elpaca-merge or C-u M-x elpaca-merge-all |
Rebuilding Packages | r x |
elpaca-rebuild Rebuild ID’s associated package.When INTERACTIVE is non-nil, prompt for ID, immediately process. With a prefix argument, rebuild current file’s package or prompt if none found. (fn ID &optional INTERACTIVE) |
Deleting Packages | d x |
elpaca-delete Remove a package associated with ID from cache and disk.If DEPS is non-nil (interactively with C-u) delete dependencies. If FORCE is non-nil (interactively with C-u C-u) do not confirm before deleting package and DEPS. (fn ID &optional FORCE DEPS IGNORED) |
View Package Logs | l filters log to current package |
elpaca-log Display ‘elpaca-log-buffer’ filtered by QUERY.(fn &optional QUERY) |
Visit Package Repository Directory | v |
elpaca-visit Open local repository directory for E with ID.When BUILD is non-nil visit build directory. (fn &optional ID BUILD) |
Visit Package Build Directory | C-u v |
C-u M-x elpaca-visit |
Browse Package Website | b |
elpaca-browse Browse menu item with ID’s :url.(fn ID) |
There is also a helpful manual.
To see how the startup time has imporoved, let's store when we start evaluating these settings.
(setq bnb/start-time (float-time))
1.2.2. Emacs Build
My current flavor of Emacs comes from: https://github.com/d12frosted/homebrew-emacs-plus
To install with brew
, run the following command:
brew install emacs-plus@29 --with-nobu417-big-sur-icon --with-imagemagick --with-native-comp
Notes: What's new in 29.1
1.2.3. Pending sections
There are some features that I like to take on a trial run. These are marked with the PENDING tag to help me remember and evaluate.
1.2.4. Performance
By utilizing elements of use-package
, I can keep an eye on
troublesome packages during startup. Together, these turn on
reporting and set the minimum time to consider when building the
report.
(setq use-package-verbose t use-package-compute-statistics t use-package-minimum-reported-time 0)
The generated messages will be found in the *Messsages*
buffer.
There is also the elpaca log that can show loading times.
2. Preamble
This sections houses the settings that need to be made up front and support subsuquent package installation and activiation.
2.1. Personal Information
The full name is used for email messages.
(setq user-full-name "Benjamin Beckwith")
2.2. Local customizations (custom.el, username.var.el)
I typically use the customize interface to generate any local settings such as proxies, paths, fonts, etc. that may vary from machine to machine. This keeps the setup the same and allows for only some details to differ.
I like to set the custom file explicitly. Mine resides in the ~/.emacs.d/ directory. This code block sets the file name and loads it if it exists.
(setq custom-file "~/.emacs.d/custom.el") (if (file-exists-p custom-file) (load-file custom-file))
If the file doesn't exist, Emacs
will still use the file if any
changes are made through the custom interface.
Sometimes I'll get bad settings or cruft in that file. I now have a
key, <F7>
, for easy navigation to wherever the custom-file
points.
2.2.1. Local variables (user-login-name)
I also intend to have a generic call to an installed local file
that may need to behave differently from custom.el
. This loads
last so that it can modify any existing setting made here to work
on the specific system in question.
In the code below, I add ~/.emacs.d/
to the load path and have a
protected call to load-library
Load the Emacs Lisp library named LIBRARY.
LIBRARY should be a string.
This is an interface to the function ‘load’. LIBRARY is searched
for in ‘load-path’, both with and without ‘load-suffixes’ (as
well as ‘load-file-rep-suffixes’).
See Info node ‘(emacs)Lisp Libraries’ for more details.
See ‘load-file’ for a different interface to ‘load’.
(fn LIBRARY). If the file exists, it gets
loaded, otherwise the error normally returned if the file is
non-existent gets ignored.
(condition-case err (progn (load-file (format "~/.emacs.d/%s.vars.el" user-login-name)) (message "Loaded local settings file %s.vars.el" user-login-name)) (file-error (message "Skipping %s.vars library as it does not exist." user-login-name)) nil)
2.3. Early Initialization
There are a few optimizations to make so that emacs can get
initialized quickly. First, setup the garbage collector to have a
default value of 16mb and a initializtion time value of
most-positive-fixnum
.
This prevents garbage collection from pausing evaluation during startup. After startup, I leverage the hook to reset the optimizations down to their default values.
The next setting stores file-name-handler-alist
Alist of elements (REGEXP . HANDLER) for file names handled specially.
If a file name matches REGEXP, all I/O on that file is done by calling
HANDLER. If a file name matches more than one handler, the handler
whose match starts last in the file name gets precedence. The
function ‘find-file-name-handler’ checks this list for a handler for
its argument.
HANDLER should be a function. The first argument given to it is the
name of the I/O primitive to be handled; the remaining arguments are
the arguments that were passed to that primitive. For example, if you
do (file-exists-p FILENAME) and FILENAME is handled by HANDLER, then
HANDLER is called like this:
(funcall HANDLER 'file-exists-p FILENAME)
Note that HANDLER must be able to handle all I/O primitives; if it has
nothing special to do for a primitive, it should reinvoke the
primitive to handle the operation "the usual way".
See Info node ‘(elisp)Magic File Names’ for more details. and then sets it to
nil
. By doing this, there is no automatic handler evalutation
happening during startup. This setting goes back to its original
value post startup.
Finally, user interface elements are hidden early to have a nice streamlined interface.
;; Disable package enabling at startup (setq package-enable-at-startup nil) ;; Tweak garbage collection threshold (defvar default-gc-cons-threshold 16777216 ; 16mb "my default desired value of `gc-cons-threshold' during normal emacs operations.") ;; make garbage collector less invasive (setq gc-cons-threshold most-positive-fixnum gc-cons-percentage 0.6) (setq default-file-name-handler-alist file-name-handler-alist file-name-handler-alist nil) (add-hook 'emacs-startup-hook (lambda (&rest _) (setq gc-cons-threshold default-gc-cons-threshold gc-cons-percentage 0.1 file-name-handler-alist default-file-name-handler-alist) ;; delete no longer necessary startup variable (makunbound 'default-file-name-handler-alist)))
The block above is written to ~/.emacs.d/early-init.el and automatically evaluated first by emacs.
2.3.1. Init debug assistance
(defmacro comment (&rest body) "Comment out sexps in BODY" nil)
2.4. Libraries
This section hosts early loading of libraries required by subsequent packages.
2.4.1. Dash
The modern list library, Dash, provides a set of common list manipulation functions (all prepended with '-', hence the name).
(use-package dash :ensure t)
2.4.2. Delight
The mode line can get pretty busy showing all of the package names. Delight helps tone it down by removing some packages from showing up, or changing their name to something shorter.
In use-package
delcarations, I use the :delight
keyword to set a
string to represent the package. It is also possible to provide
elisp for evaluation.
(use-package delight :ensure t)
2.4.3. Hydra
Sometimes it is useful to go into a command mode that lets you quickly do a few different actions. Hydra does that and more.
By defining specific hydras, you can group together commands with documentation. Think of it as a mini-control-panel. I include it here and use it elsewhere when grouping commands. (See 3.1.2.1 for an example)
(use-package hydra :ensure t)
2.4.4. Seq
Magit needs an updated seq, so we can install it here. Note that the functions below unload the library if already loaded, and then does the correct install.
;;; Take care of the seq dependency (defun +elpaca-unload-seq (e) (and (featurep 'seq) (unload-feature 'seq t)) (elpaca--continue-build e)) (defun +elpaca-seq-build-steps () (append (butlast (if (file-exists-p (expand-file-name "seq" elpaca-builds-directory)) elpaca--pre-built-steps elpaca-build-steps)) (list '+elpaca-unload-seq 'elpaca--activate-package))) (use-package seq :ensure `(seq :build ,(+elpaca-seq-build-steps)))
2.5. Coda
;;; Wait for this to be processed before packages that depend on it (elpaca-wait)
3. Settings
The sections here contain mostly settings that configure keymaps, command launchers, built-in features, and other details for day-to-day life.
3.1. Keys
These sections contain setting related to keys and keymaps.
3.1.1. Binding Keys
For binding keys, I use the bind-key package. Not only does it easily bind keys, but it does so with some nice features.
(elpaca nil (bind-keys ("C-h B" . describe-personal-keybindings) ("<f7>" . (lambda () (interactive (find-file custom-file))))))
By using bind-key
, you can specify the keystrokes that invoke a
command. In the example above, we bind functions to the global key
map. Note that in later settings, there are also examples of
mapping keys within local keymaps.
If you also want to override any possible minor-mode bindings of
the same keys, you can use bind-key*
instead.
There is also an unbind-key
to, of course, remove any binding.
The real kicker is that it will keep track of these bindings and let you see a summary of your customizations with
M-x describe-personal-keybindings
This is bound to C-h B
above.
As of emacs 28.1, there is a setting to group bindings into an
outline format. Use the following settings with M-x
describe-bindings
or C-h b
.
(setq describe-bindings-outline t)
3.1.2. Personal Keymaps
The following settings are inspired from http://endlessparentheses.com/the-toggle-map-and-wizardry.html.
- Toggle Map
This toggle map shows the current toggleable settings with shortcut keys for enabling. The amaranth color makes this buffer stay around until I press
q
.What are these settings?
Table 1: Toggle Map Functions Key Function Description c
column-number-mode
Non-nil if Column-Number mode is enabled.
See the ‘column-number-mode’ command
for a description of this minor mode.Toggle column number display in the modeline e
toggle-debug-on-error
Toggle whether to enter Lisp debugger when an error is signaled.
In an interactive call, record this option as a candidate for saving
by "Save Options" in Custom buffers.
(fn &optional INTERACTIVELY)Enter debugger on error u
toggle-debug-on-quit
Toggle whether to enter Lisp debugger when C-g is pressed.
In an interactive call, record this option as a candidate for saving
by "Save Options" in Custom buffers.
(fn &optional INTERACTIVELY)Enter debugger on C-g
f
auto-fill-mode
Toggle automatic line breaking (Auto Fill mode).
When Auto Fill mode is enabled, inserting a space at a column
beyond ‘current-fill-column’ automatically breaks the line at a
previous space.
When ‘auto-fill-mode’ is on, the ‘auto-fill-function’ variable is
non-nil.
The value of ‘normal-auto-fill-function’ specifies the function to use
for ‘auto-fill-function’ when turning Auto Fill mode on.
This is a minor mode. If called interactively, toggle the
‘Auto-Fill mode’ mode. If the prefix argument is positive,
enable the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is ‘toggle’. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate ‘auto-fill-function’.
The mode’s hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)Automatic line breaking t
toggle-truncate-lines
Toggle truncating of long lines for the current buffer.
When truncating is off, long lines are folded.
With prefix argument ARG, truncate long lines if ARG is positive,
otherwise fold them. Note that in side-by-side windows, this
command has no effect if ‘truncate-partial-width-windows’ is
non-nil.
(fn &optional ARG)Truncate long lines in the buffer r
dired-toggle-read-only
Edit Dired buffer with Wdired, or make it read-only.
If the current buffer can be edited with Wdired, (i.e. the major
mode is ‘dired-mode’), call ‘wdired-change-to-wdired-mode’.
Otherwise, toggle ‘read-only-mode’.Read-only mode w
whitespace-mode
Toggle whitespace visualization (Whitespace mode).
See also ‘whitespace-style’, ‘whitespace-newline’ and
‘whitespace-display-mappings’.
This mode uses a number of faces to visualize the whitespace; see
the customization group ‘whitespace’ for details.
This is a minor mode. If called interactively, toggle the
‘Whitespace mode’ mode. If the prefix argument is positive,
enable the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is ‘toggle’. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate ‘whitespace-mode’.
The mode’s hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG)Whitespace visualization b
orgtbl-mode
Non-nil if OrgTbl mode is enabled.
Use the command ‘orgtbl-mode’ to change this variable.Use org table minor mode (non-org buffers) x
bnb/transparency-next
Apply the next transparency value in the ring ‘bnb/transparency-ring‘.Cycle forward through transparency settings X
bnb/transparency-previous
Apply the previous transparency value in the ring ‘bnb/transparency-ring‘.Cycle backward through transparency settings B
display-battery-mode
Non-nil if Display-Battery mode is enabled.
See the ‘display-battery-mode’ command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node ‘Easy Customization’)
or call the function ‘display-battery-mode’.Show battery info in modeline l
hl-line-mode
Non-nil if Hl-Line mode is enabled.
Use the command ‘hl-line-mode’ to change this variable.Highlight current line m
bnb/hide-mode-line-mode
Minor mode to hide mode-line in current buffer
This is a minor mode. If called interactively, toggle the
‘Bnb/Hide-Mode-Line mode’ mode. If the prefix argument is
positive, enable the mode, and if it is zero or negative, disable
the mode.
If called from Lisp, toggle the mode if ARG is ‘toggle’. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate ‘bnb/hide-mode-line-mode’.
The mode’s hook is called both when the mode is enabled and when
it is disabled.Toggle mode line (elpaca nil (pretty-hydra-define hydra-toggle (:color amaranth :quit-key "q" :title " TOGGLES") ("Basic" (("c" column-number-mode "col number" :toggle t) ("l" hl-line-mode "highlight line" :toggle t) ("f" auto-fill-mode "auto-fill" :toggle t) ("t" toggle-truncate-lines "truncate lines" :toggle truncate-lines)) "Minor" (("r" rainbow-mode "rainbow" :toggle t) ("w" whitespace-mode "whitespace" :toggle t) ("b" orgtbl-mode "Org table" :toggle t) ("R" dired-toggle-read-only "dired read only" :toggle t)) "UI" (("m" bnb/hide-mode-line-mode "hide mode line" :toggle t) ("B" display-battery-mode "display battery" :toggle t) ("x" bnb/transparency-next "transparency next") ("X" bnb/transparency-previous "transparency prev")) "Emacs" (("D" toggle-debug-on-error "debug on error" :toggle (default-value 'debug-on-error)) ("X" toggle-debug-on-quit "debug on quit" :toggle (default-value 'debug-on-quit))))) (bind-key "C-x t" 'hydra-toggle/body))
- Whitespace
This mode (used in the keymap above) toggles a mode that shows the different whitespace in a buffer.
(use-package whitespace :ensure nil :commands (whitespace-mode) :custom (whitespace-line-column nil) :delight " 🟂")
- Scroll window up/down
In addition to moving the cursor, it is also interesting to scroll the screen (without moving the cursor with respect to the frame).
(defun bnb/scroll-up-1 () "Scroll up by one line." (interactive) (cua-scroll-up 1)) (defun bnb/scroll-down-1 () "Scroll down by one line." (interactive) (cua-scroll-down 1)) (elpaca nil (with-eval-after-load 'bind-key (bind-keys ("M-n" . bnb/scroll-up-1) ("M-p" . bnb/scroll-down-1))))
- Align Regexp
When selecting a region, a quick trip to
align-regexp
Align the current region using an ad-hoc rule read from the minibuffer.
BEG and END mark the limits of the region. Interactively, this function
prompts for the regular expression REGEXP to align with.
Interactively, if you specify a prefix argument, the function
will guide you through entering the full regular expression, and
then prompts for which subexpression parenthesis GROUP (default
1) within REGEXP to modify, the amount of SPACING (default
‘align-default-spacing’) to use, and whether or not to REPEAT the
rule throughout the line.
See ‘align-rules-list’ for more information about these options.
For example, let’s say you had a list of phone numbers, and wanted to
align them so that the opening parentheses would line up:
Fred (123) 456-7890
Alice (123) 456-7890
Mary-Anne (123) 456-7890
Joe (123) 456-7890
There is no predefined rule to handle this, but interactively,
all you would have to do is to mark the region, call ‘align-regexp’
and enter "(".
REGEXP must contain at least one parenthesized subexpression,
typically whitespace of the form "\\(\\s-*\\)", but in
interactive use, this is automatically added to the start of your
regular expression after you enter it. Interactively, you only
need to supply the characters to be lined up, and any preceding
whitespace is replaced.
Non-interactively, you must enter the full regular expression,
including the subexpression.
The non-interactive form of the previous example would look something like:
(align-regexp (point-min) (point-max) "\\(\\s-*\\)(")
This function is a nothing more than a small wrapper that helps you
construct a rule to pass to ‘align-region’, which does the real work.
(fn BEG END REGEXP &optional GROUP SPACING REPEAT) can align all of that nasty text.(elpaca nil (with-eval-after-load 'bind-key (bind-key "C-c TAB" 'align-regexp)))
Another great tip from Pragmatic Emacs, use kill-this-buffer
Kill the current buffer.
When called in the minibuffer, get out of the minibuffer
using ‘abort-recursive-edit’.
This command can be reliably invoked only from the menu bar,
otherwise it could decide to silently do nothing. to
kill the current buffer instead of asking which one. I'm not
overriding the C-x k
default, but added a C-x C-k
alternative.
(defun bnb/kill-this-buffer () "Kill the current buffer" (interactive) (kill-buffer (current-buffer))) (elpaca nil (bind-keys ("C-x C-k" . bnb/kill-this-buffer)))
3.1.3. Super keys
I like to be able to use the command (or super or hyper) keys for shortcuts. I need to take care to not interfere with the built-in operating system shortcuts or my bindings will not work.
(setq mac-function-modifier 'hyper mac-pass-command-to-system nil mac-right-option-modifier 'none mac-right-command-modifier 'hyper mac-right-control-modifier 'hyper mac-command-modifier 'meta mac-control-modifier 'ctrl mac-option-modifier 'super)
Note that the right option
and command
keys will pass through to
the system. This is especially cool for the option
key on a mac
that lets insert special characters directly. (E.g. á or ∑ or ®)
Inspiration for the keys comes from wisdom and wonder.
3.2. Command Launchers
This section holds the settings for my two main command launchers: hydra and vertico.
3.2.1. Hydra
Sometimes it is useful to go into a command mode that lets you quickly do a few different actions. Hydra does that and more.
By defining specific hydras, you can group together commands with documentation. Think of it as a mini-control-panel. I include it here and use it elsewhere when grouping commands. (See 3.1.2.1 for an example)
The setup is in Hydra so that I can use it with the previous keybinding commands.
3.2.2. Vertico
Or VERTical Interactive COmpletion, is my preferred completion interface.
(use-package vertico :ensure t :config (vertico-mode))
- Vertico Directory
The directory extension navigates directories like
Ido
.;; Configure directory extension. (use-package vertico-directory :after vertico :ensure nil ;; More convenient directory navigation commands :bind (:map vertico-map ("RET" . vertico-directory-enter) ("DEL" . vertico-directory-delete-char) ("M-DEL" . vertico-directory-delete-word) ("?" . minibuffer-completion-help)) ;; Tidy shadowed file names :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
- Embark
The embark package helps find actions relevant to what is near the point. With
C-.
, a menu pops up with actions to choose from.(use-package embark :ensure t :bind (("C-." . embark-act) ("C-;" . embark-dwim) ("C-x ." . embark-act) ("C-x ;" . embark-dwim) ("C-h C-b" . embark-bindings)) :init (setq prefix-help-command #'embark-prefix-help-command) :config (add-to-list 'display-buffer-alist '("\\'\\*Embark Collect \\(Live\\|Comletions\\)\\*" nil (window-parameters (mode-line-format . none))))) (use-package embark-consult :after (emark consult) :ensure t :hook (embark-collect-mode . consult-preview-at-point-mode))
- Orderless
Easy completion is possible with Orderless. This completion framework lets users utilize matching elements separated by spaces.
(use-package orderless :ensure t :custom (completion-styles '(orderless basic)) (completion-category-overrides '((file (styles basic partial-completion)))))
Within the matching framework, a few dispatchers can modify the subsequent matchers. The following table summarizes these elements.
Character Effect !
Does not match following literal ,
Matches initial characters =
Forces a literal match ~
Uses the flex matching %
Matches while ignoring diacritics - Consult
Rounding out the completion helpers, Consult provides specific functions that help complete actions or find elements. The bindings are supplied below.
(use-package consult :ensure t :bind (;; C-c bindings ("C-c h" . consult-history) ("C-c m" . consult-mode-command) ("C-c b" . consult-bookmark) ("C-c k" . consult-macro) ("C-c o" . consult-outline) ;; C-x bindings ("C-x b" . consult-buffer) ("C-x 4 b" . consult-buffer-other-window) ("C-x 5 b" . consult-buffer-other-frame) ("C-x r x" . consult-register) ("C-x r b" . consult-bookmark) ;; Custom M bindings ("M-g o" . consult-ouline) ("M-y" . consult-yank-pop) ("M-i" . consult-imenu)) :config (defvar bnb/org-agendas (list :name "Org Agenda Files" :category 'file :narrow ?a :face 'consult-file :history 'file-name-history :action #'consult--file-action :items #'org-agenda-files)) (add-to-list 'consult-buffer-sources 'bnb/org-agendas 'append) :init (fset 'multi-occur #'consult-multi-occur))
One of the more interesting feaures is virtual buffers. When viewing buffers, recent files, bookmarks, and similar, the interface shows the buffer as you are selecting so that you can have the right file context for the line you are selecting.
The
consult-buffer
Enhanced ‘switch-to-buffer’ command with support for virtual buffers.
The command supports recent files, bookmarks, views and project files as
virtual buffers. Buffers are previewed. Narrowing to buffers (b), files (f),
bookmarks (m) and project files (p) is supported via the corresponding
keys. In order to determine the project-specific files and buffers, the
‘consult-project-function’ is used. The virtual buffer SOURCES
default to ‘consult-buffer-sources’. See ‘consult--multi’ for the
configuration of the virtual buffer sources.
(fn &optional SOURCES) command is powerful and has specific key sequences that can narrow the buffer list in useful ways. These are summarized in the following list.b <SPC>
- buffers
<SPC>
- hidden buffers
* <SPC>
- modified buffers
f <SPC>
- files
r <SPC>
- file registers
m <SPC>
- bookmarks
p <SPC>
- project
In the code block above, I add one more,
a <SPC>
that will show the availableorg-agenda-files
The files to be used for agenda display.
If an entry is a directory, all files in that directory that are matched
by ‘org-agenda-file-regexp’ will be part of the file list.
If the value of the variable is not a list but a single file name, then
the list of agenda files is actually stored and maintained in that file,
one agenda file per line. In this file paths can be given relative to
‘org-directory’. Tilde expansion and environment variable substitution
are also made.
Entries may be added to this list with ‘C-c [’
and removed with ‘C-c ]’. for easy selection. - Marginalia
The great thing about vertical completion is the extra horizontal space. Marginalia makes use of this extra space by providing relevant extra information about each element on the line.
(use-package marginalia :ensure t :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle)) :init (marginalia-mode) :config (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light)))
3.3. Expansion & Completion
This section defines interations with text expansion systems.
3.3.1. Abbrev
The following block is courtesy of Endless Parentheses. For
regular misspellings, we can do ispell
Interactively check a region or buffer for spelling errors.
If ‘transient-mark-mode’ is on, and a region is active, spell-check
that region. Otherwise spell-check the buffer.
Ispell dictionaries are not distributed with Emacs. If you are
looking for a dictionary, please see the distribution of the GNU ispell
program, or do an Internet search; there are various dictionaries
available on the net. and then make an
abbreviation for future corrections.
(defun bnb/ispell-word-then-abbrev (p) "Call `ispell-word'. Then create an abbrev for the correction made. With prefix P, create local abbrev. Otherwise, it will be global." (interactive "P") (let ((bef (downcase (or (thing-at-point 'word) ""))) aft) (call-interactively 'ispell-word) (setq aft (downcase (or (thing-at-point 'word) ""))) (unless (string= aft bef) (message "\"%s\" now expands to \"%s\" %sally" bef aft (if p "loc" "glob")) (define-abbrev (if p global-abbrev-table local-abbrev-table) bef aft)))) (use-package abbrev :ensure nil :delight " ⚆" :bind (("C-x C-i" . bnb/ispell-word-then-abbrev)) :config (setq save-abbrevs t) (setq-default abbrev-mode t))
3.3.2. Cape
"Let your completions fly!" – cape.el
Cape provies a set of completion backends avaialble right on bound keys. It works with Corfu.
;; Add extensions (use-package cape :ensure t ;; Bind dedicated completion commands ;; Alternative prefix keys: C-c p, M-p, M-+, ... :bind (("C-c p p" . completion-at-point) ;; capf ("C-c p t" . complete-tag) ;; etags ("C-c p d" . cape-dabbrev) ;; or dabbrev-completion ("C-c p h" . cape-history) ("C-c p f" . cape-file) ("C-c p k" . cape-keyword) ("C-c p s" . cape-elisp-symbol) ("C-c p e" . cape-elisp-block) ("C-c p a" . cape-abbrev) ("C-c p l" . cape-line) ("C-c p w" . cape-dict) ("C-c p :" . cape-emoji) ("C-c p \\" . cape-tex) ("C-c p _" . cape-tex) ("C-c p ^" . cape-tex) ("C-c p &" . cape-sgml) ("C-c p r" . cape-rfc1345)) :init ;; Add to the global default value of `completion-at-point-functions' which is ;; used by `completion-at-point'. The order of the functions matters, the ;; first function returning a result wins. Note that the list of buffer-local ;; completion functions takes precedence over the global list. (add-to-list 'completion-at-point-functions #'cape-dabbrev) (add-to-list 'completion-at-point-functions #'cape-file) (add-to-list 'completion-at-point-functions #'cape-elisp-block) ;;(add-to-list 'completion-at-point-functions #'cape-history) ;;(add-to-list 'completion-at-point-functions #'cape-keyword) ;;(add-to-list 'completion-at-point-functions #'cape-tex) ;;(add-to-list 'completion-at-point-functions #'cape-sgml) ;;(add-to-list 'completion-at-point-functions #'cape-rfc1345) ;;(add-to-list 'completion-at-point-functions #'cape-abbrev) ;;(add-to-list 'completion-at-point-functions #'cape-dict) ;;(add-to-list 'completion-at-point-functions #'cape-elisp-symbol) ;;(add-to-list 'completion-at-point-functions #'cape-line) )
3.3.3. Corfu
Taking in-buffer completion to the next level, Corfu gives familar functionality with nice enhancements. It integrates with orderless for easier searching, and has the ability to show documentation alongside of the completion popup.
(use-package corfu :ensure t :custom (corfu-auto nil) (tab-always-indent 'complete) :bind (:map corfu-map ("SPC" . corfu-insert-separator)) :init (global-corfu-mode) (corfu-popupinfo-mode 1))
3.3.4. Yasnippet PENDING
Text expansion makes sense in many programming modes. Yasnippet comes in handy by providing a minor mode for easy expansions.
(use-package yasnippet :ensure t :defer 30 :hook (prog-mode . yas-minor-mode) (text-mode . yas-minor-mode) :config (yas-reload-all))
I also load a collection of yasnippet snippets so I don't have to maintain my own.
(use-package yasnippet-snippets :ensure t)
3.3.5. Hippie Expand
Try to expand the text before point in an intelligent way. Repeat the keypress to cycle through options.
(elpaca nil (bind-key "M-/" 'hippie-expand))
3.4. Built-in Features
Emacs comes with some nice batteries. This section configures my favorites.
3.4.1. Backups
Sensible backup settings from https://www.emacswiki.org/emacs/BackupDirectory
(setq backup-by-copying t create-lockfiles nil backup-directory-alist '((".*" . "~/.emacs.d/.saves")) ;; auto-save-file-name-transforms `((".*" "~/.saves" t)) kill-buffer-delete-auto-save-files t delete-old-versions t kept-new-versions 6 kept-old-versions 2 version-control t)
Here's a quick rundown of the settings:
backup-by-copying
Non-nil means always use copying to create backup files.
See documentation of variable ‘make-backup-files’.- Use copying to create backups when
t
create-lockfiles
Non-nil means use lockfiles to avoid editing collisions.
The name of the (per-buffer) lockfile is constructed by prepending
".#" to the name of the file being locked. See also ‘lock-buffer’ and
Info node ‘(emacs)Interlocking’.- Don't use lockfiles if
nil
backup-directory-alist
Alist of file name patterns and backup directory names.
Each element looks like (REGEXP . DIRECTORY). Backups of files with
names matching REGEXP will be made in DIRECTORY. DIRECTORY may be
relative or absolute. If it is absolute, so that all matching files
are backed up into the same directory, the file names in this
directory will be the full name of the file backed up with all
directory separators changed to ‘!’ to prevent clashes. This will not
work correctly if your filesystem truncates the resulting name.
For the common case of all backups going into one directory, the alist
should contain a single element pairing "." with the appropriate
directory name.
If this variable is nil, or it fails to match a file name, the backup
is made in the original file’s directory.
On MS-DOS filesystems without long names this variable is always
ignored.- List of regexp/location pairs of where to backup files
kill-buffer-delete-auto-save-files
If non-nil, offer to delete any autosave file when killing a buffer.
If ‘delete-auto-save-files’ is nil, any autosave deletion is inhibited.- Killing a buffer with an auto-save file will prompt for deletion
delete-old-versions
If t, delete excess backup versions silently.
If nil, ask confirmation. Any other value prevents any trimming.- Delete excess backups silently if
t
kept-new-versions
Number of newest versions to keep when a new numbered backup is made.
Includes the new backup. Must be greater than 0.- Number of newest versions to keep
kept-old-versions
Number of oldest versions to keep when a new numbered backup is made.- Number of oldest versions to keep
version-control
Control use of version numbers for backup files.
When t, make numeric backup versions unconditionally.
When nil, make them for files that have some already.
The value ‘never’ means do not make them.- When
t
, make numeric backup versions always
3.4.2. Default File encoding
I like to have the files be utf-8
by default. Do
let me know if I shouldn't do this, will you?
Set utf-8
for all coding systems except for the clipboard on
windows. That one gets utf-16le
to be compatible.
(prefer-coding-system 'utf-8) (set-default-coding-systems 'utf-8) (set-terminal-coding-system 'utf-8) (set-keyboard-coding-system 'utf-8) (set-language-environment 'utf-8) (setq buffer-file-coding-system 'utf-8 x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) ;; MS Windows clipboard is UTF-16LE (when (eq system-type 'windows-nt) (set-clipboard-coding-system 'utf-16le-dos))
3.4.3. Native Compilation
Emacs 28.1 introduced Native Compilation. When this feature is available, I use it to compile the packages.
There are also two settings to make the process slightly more verbose and ensure that warnings and erros are bubbled up from any async processes.
(if (native-comp-available-p) (setq package-native-compile t native-comp-verbose 1 native-comp-async-report-warnings-errors t))
3.4.4. Path
Sometimes Emacs' idea of path differs from the shell. The package exec-path-from-shell seeks to bring those in line with each other.
(use-package exec-path-from-shell :ensure t :defer t :config (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize)))
3.4.5. Server
Using Emacs as a server is a great way to keep the power responsive.
(when (and (or (eq system-type 'windows-nt) (eq system-type 'darwin)) (not (and (boundp 'server-clients) server-clients)) (not (daemonp))) (server-start))
3.4.6. Sounds
I dislike the bell ringing when I hit C-g
. To silence the bell,
just set the ring-bell-function
to nil
.
(setq visual-bell nil ring-bell-function `(lambda () nil))
3.4.7. Recentf
I enable emacs remembering recently open files. For my setup, this feeds into the candidates for Consult.
(elpaca nil (recentf-mode t))
3.4.8. Timezones
For world-clock
Display a world clock buffer with times in various time zones.
The variable ‘world-clock-list’ specifies which time zones to use.
To turn off the world time display, go to the window and type ‘M-x quit-window’., it's best to define the time zones most relevant
to me. For compatible time zones, check this handy list.
(setq zoneinfo-style-world-list '(("America/New_York" "CBUS") ("America/Los_Angeles" "San Fran") ("Europe/London" "London") ("Australia/Sydney" "Sydney") ("Asia/Kolkata" "Bangalore")))
3.4.9. Isearch
Folding quotes will allow isearch to find similar characters to the ones being searched for.
;; New in Emacs 29 (setq isearch-fold-quotes-mode t)
3.5. Minibuffer
This section holds any minibuffer
settings.
3.5.1. Minibuffer History
Let's get rid of duplicates in the minibuffer history.
(setq history-delete-duplicates t)
This saves the minibuffer histories to preserve across emacs sessions.
(elpaca nil (setq savehist-additional-variables '(search-ring regexp-search-ring) savehist-file "~/.emacs.d/savehist") (savehist-mode t))
3.6. Movement
Getting around takes a little tweaking. This section holds the details on how movement is defined for me.
3.6.1. Ace Utilities
The Ace (and subsequent Avy) packages aid in jumping the cursor to the right place in the buffer.
- Ace Flyspell
Turn on ace-flyspell when flyspell is enabled. This mode helps jump between the errors (misspellings) discovered by flyspell.
(use-package ace-flyspell :after (hydra major-mode-hydra) :ensure t :commands (ace-flyspell-setup) :bind ("H-s" . hydra-fly/body) :hook (flyspell-mode . ace-flyspell-setup) :init (pretty-hydra-define hydra-fly (:color pink :quit-key "q" :title " Flyspell") ("Checking" (("b" flyspell-buffer "Check buffer") ("r" flyspell-region "Check region")) "Correction" (("c" ispell-word "Correct word") ("." ace-flyspell-dwim "dwim")) "Movement" (("n" flyspell-goto-next-error "Next error") ("j" ace-flyspell-jump-word "Jump word")))))
- Ace Isearch
Supercharge
isearch
to vary its behavior depending on the input. TheC-'
key let's me jump to the isearch match easily with theace-jump
methods.(use-package ace-isearch :ensure t :bind (:map isearch-mode-map ("C-'" . ace-isearch-jump-during-isearch)) :delight ace-isearch-mode :config (global-ace-isearch-mode t) (setq ace-isearch-input-length 8))
- Ace Link
In modes with links, use
o
to jump to links. MapM-o
to do the same in Orgmode.(use-package ace-link :ensure t :bind (:map org-mode-map ("M-o" . ace-link-org)) :config (ace-link-setup-default))
- Ace Window
Instead of
C-x o
traversal,ace-window
provides numbers for quick window accessSet the keys to something other than the default numbers. Note that this also limits the number of windows that can be used, but given my usage, I doubt it goes up to 'm' often.
Also, I modify the face attribute to make the window numbers large.
After reading the wiki, I supercharged the interface for
ace-window
.(use-package ace-window :ensure t :bind ("H-SPC" . ace-window) ("<f9> a" . ace-window) :custom (aw-keys '(?j ?k ?l ?\; ?a ?s ?d ?f)) (aw-leading-char-style 'path) (aw-dispatch-always t)) (elpaca nil (progn (pretty-hydra-define hydra-window-controls (:color amaranth :quit-key "q" :title " Window controls") ("Window Size" (("h" shrink-window-horizontally "shrink horizontal") ("j" shrink-window "shrink vertical") ("k" enlarge-window "enlarge vertical") ("l" enlarge-window-horizontally "enlarge horizontal")) "Scroll other window" (("n" scroll-other-window "scroll") ("p" scroll-other-window-down "scroll down")))) (pretty-hydra-define hydra-frame-controls (:color red :title " Frame controls") ("Modification" (("f" make-frame "new frame") ("x" delete-frame "delete frame")))) (with-eval-after-load 'ace-window (progn (add-to-list 'aw-dispatch-alist '(?w hydra-window-controls/body) t) (add-to-list 'aw-dispatch-alist '(?F hydra-frame-controls/body) t) (add-to-list 'aw-dispatch-alist '(?B balance-windows) t) (set-face-attribute 'aw-leading-char-face nil :height 2.0)))))
- Avy Goto
Navigating to the right spot in a buffer can be done in an easy fashion with Avy. The collection of goto functions yield a variety of methods to select where to place the point.
In the set of mappings below, it's easy to see the thing you are targeting (word, char, line), and how you are targeting it. The how is the suffix.
A suffix of
1
means you will input one character to show the candidates. A suffix of0
will list all candidates without an initial selection. A suffix of2
means you'll input two characters before showing candidates. Finally, a suffix oftimer
will accept several characters and then show the candidates after an elapsed timer.(use-package avy :ensure t :bind ("H-." . avy-goto-char-timer) ("H-w" . avy-goto-word-1) ("H-/" . avy-goto-char-2) ("H-l" . avy-goto-line) ("H-d" . avy-goto-word-0) ("<f9> ." . avy-goto-char-timer) ("C-c g" . avy-goto-word-1) ("M-g l" . avy-goto-line) ("M-g ." . avy-goto-char-2) ("M-g w" . avy-goto-word-0))
The commands begin with the normal prefix of
M-g
for the goto commands and use l,c and w for lines, characters and words respectively. - Avy Zap
Zap to char using avy. This is just what is sounds like. You kill everything from point to the selected character.
(use-package avy-zap :ensure t :bind ("M-z" . avy-zap-to-char-dwim) ("M-Z" . avy-zap-up-to-char-dwim))
3.6.2. Errors
When navigating errors (output from M-x compile
for example), this
highlights the visited error. Although named for errors, this
functionality is also used for M-x occur
and M-x rgrep
and others.
Within the buffer full of errors or matches, M-g M-n/M-p
will
navigate up/down visiting the errors in a separate buffer and
highlighting the current error or match.
(setq next-error-message-highlight t)
3.6.3. Read-only helpers
For read-only files, look at them in view-mode
Non-nil if View mode is enabled.
Use the command ‘view-mode’ to change this variable. which will enable
vi-style navigation. In this mode, kill commands will save text,
but not remove it.
(use-package view :ensure nil :delight " 👁" :init (setq view-read-only t) :bind (:map view-mode-map ("n" . next-line ) ("p" . previous-line) ("j" . next-line ) ("k" . previous-line) ("l" . forward-char) ("h" . bnb/view/h) ("q" . bnb/view/q)) :config (defun bnb/view/h () "Setup a function to go backwards a character" (interactive) (forward-char -1)) (defun bnb/view/q () "Setup a function to quit `view-mode`" (interactive) (view-mode -1)))
3.6.4. Scroll Screen Position
This is one of those cool finds for a problem I mostly knew that I
had. I often hit C-v
by accident and lose my place. With the
following setting, M-v
completely undoes the scroll leaving the
cursor back in the original position.
(setq scroll-preserve-screen-position 'always)
Thanks to http://irreal.org/blog/?p=3963 for the tip.
3.7. Custom Helpers
This is a collection of code specific to how I use emacs. Some are from different websites or other Emacs users.
3.7.1. Auto-display agenda
John Weigley shows a way to display the agenda after some period of inactivity.
(defun bnb/jump-to-org-agenda () "Create and jump to the bnb org agenda." (interactive) (let ((buf (get-buffer "*Org Agenda*")) wind) (if buf (if (setq wind (get-buffer-window buf)) (select-window wind) (if (called-interactively-p) (progn (select-window (display-buffer buf t t)) (org-fit-window-to-buffer)) (with-selected-window (display-buffer buf) (org-fit-window-to-buffer)))) (org-agenda nil "f.")))) (defun bnb/idle-agenda (&optional arg) "Set or cancel idle agenda timer based on [ARG]." (interactive "P") (setq bnb/iagenda (if arg (cancel-timer bnb/iagenda) (run-with-idle-timer 3600 t 'bnb/jump-to-org-agenda)))) (elpaca nil (bnb/idle-agenda))
3.7.2. Auto-indent when pasting
Automatically indent pasted blocks of text.
(dolist (command '(yank yank-pop)) (eval `(defadvice ,command (after indent-region activate) (and (not current-prefix-arg) (let ((mark-even-if-inactive transient-mark-mode)) (indent-region (region-beginning) (region-end) nil))))))
3.7.3. Better window splitting functions
http://www.reddit.com/r/emacs/comments/25v0eo/you_emacs_tips_and_tricks/chldury
These settings split the window and load a previous buffer (instead of the same buffer in both). This has a better chance of being what I want when splitting strings.
(defun bnb/vplit-last-buffer () "When splitting the frame, load the last visited buffer." (interactive) (split-window-vertically) (other-window 1 nil) (switch-to-next-buffer)) (defun bnb/hsplit-last-buffer () "When splitting the frame, load the last visited buffer." (interactive) (split-window-horizontally) (other-window 1 nil) (switch-to-next-buffer)) (elpaca nil (bind-keys ("C-x 2" . bnb/vplit-last-buffer) ("C-x 3" . bnb/hsplit-last-buffer)))
3.7.4. Hide mode line
This is a fun one I picked from a now defunct website. This block of code hides the mode-line for the current buffer (window).
;; Setup buffer-local behavior (defvar-local bnb/hide-mode-line-mode nil) ;; Setup minor mode (define-minor-mode bnb/hide-mode-line-mode "Minor mode to hide mode-line in current buffer" :init-value nil :global nil :variable bnb/hide-mode-line-mode :group 'editing-basics (if bnb/hide-mode-line-mode (setq bnb/hide-mode-line-mode/saved-format mode-line-format mode-line-format nil) (setq mode-line-format bnb/hide-mode-line-mode/saved-format bnb/hide-mode-line-mode/saved-format nil)) (force-mode-line-update) (redraw-display) (when (and (called-interactively-p 'interactive) bnb/hide-mode-line-mode) (run-with-idle-timer 0 nil 'message (concat "Goodbye mode line!" "Use M-x bnb/hide-mode-line-mode to make the mode-line appear")))) ;; Bind global key (elpaca nil (bind-key "H-0" 'bnb/hide-mode-line-mode))
3.7.5. Open/Edit This file
When I hit <F5>
, open this file for editing. That way, any
time I have something I need to remember for my emacs setting, it
is just a button-push away.
(elpaca nil (bind-key "<f5>" (lambda () (interactive) (find-file "~/.emacs.d/bnb-emacs/Readme.org"))))
3.7.6. Org-column resizing
In order to resize the face when `org-column` mode is on, some
advice is in order. The face used has a set :height
that is not
overridden by custom face settings.
To have a custom height, this advice prepends the list with an anonymous face with a height of 0.8. This setting happens first, so it wins.
(defun bnb/org-overlay-font-override (orig-fn beg end &optional txt face) (let ((bnbface (cons '(:height 0.8) face))) (funcall orig-fn beg end txt bnbface))) (with-eval-after-load 'org (advice-add 'org-columns--new-overlay :around #'bnb/org-overlay-font-override)) ;(advice-remove 'org-columns--new-overlay #'bnb/org-overlay-font-override)
3.7.7. Prettify macro
There is a little bit of boilerplate to get the right set or replacements set for prettify correctly. This is exactly why macros are a thing. This one simplifies the call to provide a mode, and the list of replacements.
(defmacro bnb/prettify (mode replacements) "Set the prettify REPLACEMETS for MODE in a mode hook" `(progn (setq ,(intern (concat "bnb/prettify-" mode "-replacements")) ,replacements) (defun ,(intern (concat "bnb/prettify-" mode "-setup")) () (mapc (lambda (pair) (push pair prettify-symbols-alist)) ,(intern (concat "bnb/prettify-" mode "-replacements"))) (prettify-symbols-mode t)) (add-hook (quote ,(intern (concat mode "-hook"))) (function ,(intern (concat "bnb/prettify-" mode "-setup"))))))
3.7.8. Styled HTML Export
This is how I get the one-page html output for Github Pages. There are two main parts to setting up and executing the export.
First, I use a SETUPFILE
from
https://github.com/fniessen/org-html-themes. Specifically, I use
the readtheorg style.
Second, I setup the emacs theme correctly for nice code output. Syntax highlighting in the export will pull from the current theme. I don't want this. Instead, I want to specify which theme to use for every export.
The code below stores away the current list of enabled themes
before disabling them all. Then, it enables my preferred export
theme (sanityinc-tomorrow-day
) before performing the
export. Finally, it disables the last theme and renables all of the
ones on the list.
(defvar bnb/export-theme '(sanityinc-tomorrow-day)) (defun bnb/export-readme () "Export the tangled org setting as html. `bnb/export-theme` sets the theme for the code exports." (interactive) (let ((themes custom-enabled-themes) (file "~/.emacs.d/bnb-emacs/Readme.org")) (with-temp-buffer (insert "#+SETUPFILE: https://fniessen.github.io/org-html-themes/org/theme-readtheorg.setup\n") (insert (format "#+include: %s\n" (file-truename file))) (org-mode) (elpaca-wait) ;; ensure all modules are loaded (mapc 'disable-theme themes) (mapc 'load-theme bnb/export-theme) (let ((exported (org-export-as 'html)) (save-silently-p t)) (with-temp-file (format "%sindex.html" (file-name-directory file)) (insert exported)) (mapc 'disable-theme bnb/export-theme) (mapcar 'load-theme (reverse themes))))))
The process is to create a temp buffer and insert the setupfile
and an include to this file. Some of the finer points are that I
ensure org-mode
is on, themes are loaded/unloaded correctly and
the export goes to the right file.
Not all of the links I use in this file easily export. Some require some tweaking to show up nicely on the web. This next block sets up some handlers for the link types that need a little extra care and attention.
(defun bnb/export-tooltip (link description format) "Exporter for help: links" (let ((desc (or description link))) (pcase format ('html (format "<span class=\"tooltip\"><code>%s</code>%s</span>" desc (bnb/make-doc-tooltip desc))) (_ desc)))) (defun bnb/space-to-html-entity (text) "Change spaces to html entities in TEXT." (string-replace " " " " text)) (defun bnb/linebreak-to-html-entity (text) "Change linebreaks to html entities in TEXT." (string-replace "\n" "<br>" text)) (defun bnb/html-entity-replacement (text) "Perform html entity conversions on TEXT." (bnb/linebreak-to-html-entity (bnb/space-to-html-entity text))) (defun bnb/make-doc-tooltip (element) "Pop out tooltip text if we have it" (condition-case err (let* ((template "<span class=\"tooltiptext\">%s</span>") (sym (intern element)) (doc (if (symbolp sym) (or (documentation-property sym 'variable-documentation) (documentation sym)) ""))) (format template (bnb/html-entity-replacement doc))) (error (message "Skipping Error: %s" err)))) (defun bnb/export-help-links (link description format) (bnb/export-tooltip link description format)) (defun bnb/export-org-ql-links (link description format) (let ((desc (or description link))) (pcase format ('html (format "<span class=\"tooltip\"><code>%s</code>%s</span>" desc "<span class=\"tooltiptext\">Org QL search links only work in Emacs.</span>")) (_ desc)))) (with-eval-after-load 'org (org-link-set-parameters "help" :export #'bnb/export-help-links) (org-link-set-parameters "org-ql-search" :export #'bnb/export-org-ql-links))
3.7.9. Transparency
Using the ring
package, these commands will cycles through
transparency settings.
The transparency ring variable holds cells that determing the focused and unfocused opacity settings in terms of percentage.
(use-package ring :ensure nil :commands (bnb/transparency-apply bnb/transparency-next bnb/transparency-previous bnb/transparency-cycle bnb/transparency-add) :config (setq bnb/transparency-ring (ring-convert-sequence-to-ring (list '(100 100) '(100 50) '(100 10) '(95 50) '(90 50) '(85 50))) bnb/transparency (ring-ref bnb/transparency-ring 0)) (defun bnb/transparency-apply (trans) "Apply the TRANS alpha value to the frame." (set-frame-parameter (selected-frame) 'alpha (setq bnb/transparency trans))) (defun bnb/transparency-next () "Apply the next transparency value in the ring `bnb/transparency-ring`." (interactive) (bnb/transparency-apply (ring-next bnb/transparency-ring bnb/transparency))) (defun bnb/transparency-previous () "Apply the previous transparency value in the ring `bnb/transparency-ring`." (interactive) (bnb/transparency-apply (ring-previous bnb/transparency-ring bnb/transparency))) (defun bnb/transparency-cycle () "Cycle to the next transparency setting." (interactive) (bnb/transparency-next)) (defun bnb/transparency-add (active inactive) "Add ACTIVE and INACTIVE transparency values to the ring." (interactive "nActive Transparency:\nnInactive Transparency:") (ring-insert+extend bnb/transparency-ring (list active inactive) t) (bnb/transparency-apply (list active inactive))))
3.7.10. Weekly Score Goal in Org-Agenda
I use a scoring system to keep track of my overall progress. This involves scoring my tasks and attributing my idea of impact of a particular done item.
To use these numbers, I do a weekly review on Monday and compare the numbers to past years/weeks/etc. To keep pushing forward, this little bit of code will insert a running status at the top of my agenda.
If I am on track for the given day (at or above the scaled goal), all is green. Otherwise, I get a warning type formatting above 80% and error type formatting under.
;; Define my goal to hit (defvar bnb/weekly-score-goal 42) ;; Add up all the scores from DONE items in the agenda files (defun bnb/agenda-score-goal () "Add up scores from done items. In the agenda, this will show the number of done items and the target goal from `bnb/weekly-score-goal`." (let* ((score ;; Add up all scores from DONE items (apply '+ (org-map-entries (lambda () (string-to-number (or (org-entry-get (point) "Score") "0"))) "/DONE" 'agenda))) (scaled-goal (* bnb/weekly-score-goal (/ (string-to-number (format-time-string "%w")) 5.0))) (face (cond ((>= score scaled-goal) 'success) ((>= score (* .8 scaled-goal)) 'warning) (t 'error))) (goal-label (format "✧ Score Goal (%d): " scaled-goal)) (goal-metric (format "%d/%d\n" score bnb/weekly-score-goal)) (header-size (+ (string-width goal-label) (string-width goal-metric))) (goal-separator (concat (make-string header-size ?┄) "\n"))) (insert (concat (propertize goal-label 'face 'org-agenda-structure) (propertize goal-metric 'face face) (propertize goal-separator 'face 'org-agenda-structure))))) ;; This hook runs first in the agenda (and before it is set to read-only) (with-eval-after-load 'org (add-hook 'org-agenda-mode-hook 'bnb/agenda-score-goal))
3.7.11. Weekly Time Reporting
This is a function to create an entry like a datetree, but using years and workweeks instead.
(defun bnb/find-year-create (year) "Find or create a [YEAR] in an Org journal." (let ((re "^\\**[ \t]+\\([12][0-9]\\{3\\}\\)") match) (org-narrow-to-subtree) (goto-char (point-min)) (while (and (setq match (re-search-forward re nil t)) (goto-char (match-beginning 1)) (< (string-to-number (match-string 1)) year))) (cond ((not match) (goto-char (point-max)) (or (bolp) (newline)) (insert (format "** %s\n" year))) ((= (string-to-number (match-string 1)) year) (goto-char (point-at-bol))) (t (beginning-of-line 1) (insert (format "** %s\n" year)))))) (defun bnb/find-ww-create (ww) "Find or create a [WW] (workweek) in an Org journal." (let ((re "^\\**[ \t]+\\WW\\([0-9]\\{2\\}\\)") match) (org-narrow-to-subtree) (goto-char (point-min)) (while (and (setq match (re-search-forward re nil t)) (goto-char (match-beginning 1)) (< (string-to-number (match-string 1)) ww))) (cond ((not match) (goto-char (point-max)) (or (bolp) (newline)) (insert (format "*** WW%02d\n" ww))) ((= (string-to-number (match-string 1)) ww) (goto-char (point-at-bol))) (t (beginning-of-line 1) (insert (format "*** WW%02d\n" ww)))))) (defun bnb/insert-weekly-time-sheet () "Generated and insert a weekly time sheet generated from the default Org Agenda." (with-temp-buffer (insert (concat "#+BEGIN: clocktable :maxlevel 3 :scope agenda-with-archives :block lastweek :fileskip0 t :properties (\"Score\") :indent nil \n" "#+TBLFM: $6='(org-clock-time% @2$4 $3..$5);%.1f::@2$2=vsum(@3$2..@>$2)\n" "#+END:\n\n")) (goto-char (point-min)) (org-update-dblock) (buffer-substring (point-min) (point-max)))) (defun bnb/insert-weekly-clocking () "Insert the weekly clocking clocking data." (let ((year (number-to-string (nth 2 (calendar-gregorian-from-absolute (org-today))))) (ww (bnb/workweek))) (goto-char (point-min)) (goto-char (cdr (org-id-find "clocking"))) (bnb/find-year-create (string-to-number year)) (bnb/find-ww-create ww)))
3.7.12. Workweeks
This is vestigal content from my Intel days and this generates their idea of a work week number.
(elpaca nil (progn (defun bnb/workweek () "Return the current workweek number." (interactive) (let* ((now (current-time)) (weeks (string-to-number (format-time-string "%W" now))) (days (time-to-day-in-year now)) (daynum (string-to-number (format-time-string "%w" now))) (left (% days 7))) (if (and (= 0 daynum) (= left 0)) weeks (+ 1 weeks)))) (defun bnb/workweek-string () "Convert the current workweek into a string. The string is of the format WW##." (interactive) (concat "WW" (number-to-string (bnb/workweek)))) (require 'calendar) (defun bnb/workweek-from-gregorian (&optional date) "Calculate the workweek from the Gregorian calendar." (let* ((date (or date (calendar-current-date))) (year (calendar-extract-year date)) (fst (calendar-day-of-week (list 1 1 year))) (x (if (>= fst 4)1 0))) (+ x (car (calendar-iso-from-absolute (calendar-absolute-from-gregorian date)))))) (setq calendar-week-start-day 1 calendar-intermonth-text '(propertize (format "%2d" (bnb/workweek-from-gregorian (list month day year))) 'font-lock-face 'font-lock-function-name-face))))
3.8. Editing
Similar to movement, editing happens every day, so I use a few customizations to make it nice.
3.8.1. Ediff single frame
I really dislike the multi-frame mode of ediff
. It is confusing
to use and really messes up my dwm usage. By explicitly setting
the following setting, it forces ediff
to use only one
frame.
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
Now the control window will be a small window instead of a separate frame.
3.8.2. Multiple Cursors
This interface is a mix of an example on the hydra wiki and my own additions.
I think that the key thing is remembering to not have this affect all cursors when prompted. Otherwise, it seems, the cursors are duplicated in strange ways.
(use-package multiple-cursors :ensure t :bind ("H-m" . hydra-mc/body) ("C-x m" . hydra-mc/body) ("s-<mouse-1>" . mc/add-cursor-on-click) ("C-x M" . compose-mail) :config (pretty-hydra-define hydra-mc (:hint nil :title " Multiple cursors" :quit-key "q") ("Down" (("n" mc/mark-next-like-this "Mark next line") ("N" mc/skip-to-next-like-this "Skip next line") ("M-n" mc/unmark-next-like-this "Unmark line going down")) "Up" (("p" mc/mark-previous-like-this "Mark previous line") ("P" mc/skip-to-previous-like-this "Skip previous line") ("M-p" mc/unmark-previous-like-this "Unmark line going up")) "Mark many" (("l" mc/edit-lines "Convert region") ("a" mc/mark-all-like-this-dwim :exit t "Mark all like selection") ("g" mc/mark-all-in-region-regexp :exit t "Mark regexp in region") ("r" mc/mark-sgml-tag-pair :exit t "Mark tag pair") ("x" mc/mark-more-like-this-extended "Extended marking")) "Special" (("1" mc/insert-numbers "Insert numbers") ("^" mc/sort-regions "Sort regions") ("|" mc/vertical-align "Vertially align") ("A" mc/insert-numbers "Insert letters")))))
3.8.3. Regexp-Builder
Emacs regular expressions are not the easiest to use out of the
box. Emacs now has regexp-builder
Construct a regexp interactively.
This command makes the current buffer the "target" buffer of
the regexp builder. It displays a buffer named "*RE-Builder*"
in another window, initially containing an empty regexp.
As you edit the regexp in the "*RE-Builder*" buffer, the
matching parts of the target buffer will be highlighted.
Case-sensitivity can be toggled with M-x reb-toggle-case. The
regexp builder supports three different forms of input which can
be set with M-x reb-change-syntax. More options and details are
provided in the Commentary section of this library. to assist you in building the
correct regexp as you type.
However, to complicate matters, there are five different syntaxes
of regular expression that the builder can use. The string
syntax is what I tend to use most in searching and replacing, so I
will make that my default.
(setq reb-re-syntax 'string)
Key Binding | Meaning |
---|---|
C-c TAB |
Switch syntax |
C-c C-e |
Sub-expression mode (show matching groups) |
C-c C-s/r |
Search forward/backward |
C-c C-w |
Copy regexp to kill ring |
C-c C-q |
Quit the builder |
Be sure to consult the syntax of regular expressions to learn more about the weird backslashing.
3.8.4. Executable Scripts on save
Taken from http://mbork.pl/2015-01-10_A_few_random_Emacs_tips, this setting makes a file executable if it's a script.
(add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p)
3.8.5. Auto Reverting
For view-only buffers rendering content, it is useful to have them
auto-revert
in case of changes.
(add-hook 'doc-view-mode-hook 'auto-revert-mode) (add-hook 'image-mode 'auto-revert-mode)
3.9. Images
Emacs does a good job with images, so any particular preferences are handled in this section.
3.9.1. ImageMagick
Register image file types that can be handled by ImageMagick. Note that Emacs needs to be compiled with ImageMagick support for this to do anything.
(elpaca nil (when (fboundp 'imagemagick-register-types) (imagemagick-register-types)))
4. Minor Modes
The minor modes can be used in a variety of situations to enhance the editing experience overall.
4.1. Reference
These modes help present reference material.
4.1.1. Helpful
Using Helpful enables a better help buffer by providing a more organized screen with contextual information and linked references.
(use-package helpful :ensure t :bind ("C-h K" . helpful-key) ("C-h v" . helpful-variable) ("C-h f" . helpful-function) ("C-h x" . helpful-command) ("C-h z" . helpful-macro) ("C-h ." . helpful-at-point))
4.1.2. Which Key
This helpful little package makes it easy to remember emacs
prefixed commands. Start typing a prefix such as C-x
after a brief
delay, the options for any following commands are shown.
I am using a setup that tries the right side of emacs first, and punts to a bottom window if there is not enough room.
(use-package which-key :defer t :ensure t :delight which-key-mode :init (which-key-mode) (which-key-setup-side-window-right-bottom) :custom (which-key-max-description-length 60))
4.1.3. Dictionary
To use the online dictionary at dict.org, set dictionary-server
accordingly. Then swap around keybindinds such that this is an easy
deafult, but the OSX version isn't far away.
One of the cooler features of this mode is that the dict.org server has Webster's 1913 dictionary.
(use-package dictionary :init (setq dictionary-server "dict.org") :bind (("C-c d" . dictionary-search) ("C-c D" . osx-dictionary-search-word-at-point)))
4.1.4. OSX Dictionary
Search Dictionary.app
from the comfort of an Emacs buffer.
(use-package osx-dictionary :ensure t :bind ("C-c d" . osx-dictionary-search-word-at-point) ("C-c i" . osx-dictionary-search-input))
4.2. Editing
This section covers minor modes that personalize and improve the editing experience.
4.2.1. Adaptive Fill PENDING
Update: Turning this off for now to see if I really use it for just text modes.
Try to keep any prefixed elements of the first line for paragraph filling.
(use-package filladapt :delight " ▦" :disabled t :ensure t :commands filladapt-mode :init (setq-default filladapt-mode t) :hook ((text-mode . filladapt-mode) (org-mode . turn-off-filladapt-mode) (prog-mode . turn-off-filladapt-mode)))
4.2.2. Focus Mode
Dim everything except for the thing-at-point. Improves focus when reading code and text.
(use-package focus :ensure t :bind ("C-c f" . focus-mode) ("C-c F" . focus-read-only-mode))
4.2.3. Common User Access mode
CUA has a primary feature of enabling cut, copy, paste and undo shortcuts compatible with most applications, but I leave that part disabled and prefer the normal emacs bindings.
What I do enjoy about CUA are the rectangle restures and that is why I enable it.
(elpaca nil (progn (cua-mode t) (setq cua-enable-cua-keys nil)))
- CUA Rectangles
These take place with an active rectangle. To start/cancel a rectangle use
C-RET
.Keys Function M-<arrow>
Move rectangle overlay C-<SPACE>
Activate region bounded by rectangle M-a
Align all words at the left edge M-b
Fill rectangle with blanks (tabs and spaces) M-c
Closes the rectangle by removing left edge blanks M-f
Fills the rectangle with a single character (prompt) M-i
Increases number found on each line of rectangle M-k
Kills the rectangle as normal multi-line kill M-l
Downcases the rectangle M-m
Copies the rectangle for normal multi-line paste M-n
Fills each line with increasing numbers (prompt) M-o
Opens the rect by moving hilighted text right and filling with blanks M-p
Toggles virtual straight rectangle edges M-P
Inserts tabs and spaces to make real straight edges M-q
Performs text filling on the rectangle M-q
Performs text filling on the rectangle M-r
Replaces REGEXP (prompt) by STRING (prompt) in rectangle M-R
Reverse the lines in the rectangle M-s
Fills each line of the rectangle with the same STRING (prompt) M-t
Performs text fill of the rectangle with TEXT (prompt) M-u
Upcases the rectangle M-<VBar>
Runs shell command on rectangle M-'
Restricts rectangle to lines with CHAR (prompt) at left column M-/
Restricts rectangle to lines matching REGEXP (prompt) C-?
Shows a brief list of the above commands. M-C-<UP/DOWN>
Scrolls the lines INSIDE the rectangle up/down - CUA Global Mark
The global mark feature enables a target the receives any typed/copied/killed text from any buffer (even the current one).
Key function <ch>
All characters (including newlines) you type are inserted at the global mark! C-x
If you cut a region or rectangle, it is automatically inserted at the global mark, and the global mark is advanced. C-c
If you copy a region or rectangle, it is immediately inserted at the global mark, and the global mark is advanced. C-v
Copies a single character to the global mark. C-d
Moves (i.e. deletes and inserts) a single character to the global mark. <BACKSPACE>
deletes the character before the global mark <DELETE>
deletes the character after the global mark. S-C-space
Jumps to and cancels the global mark. C-u S-C-space
Cancels the global mark (stays in current buffer). <TAB>
Indents the current line or rectangle to the column of the global mark.
4.2.4. Easy Kill
While looking for a way to store the filename in the clipboard, I ran across easy-kill. Not only will it grab the filename, but provides ways to grab all sorts of fun things.
(use-package easy-kill :bind ("M-w" . easy-kill) :ensure t)
The way the binding works is as a prefix key that also tries a
default "kill" and replaces kill-ring-save
Save the region as if killed, but don’t kill it.
In Transient Mark mode, deactivate the mark.
If ‘interprogram-cut-function’ is non-nil, also save the text for a window
system cut and paste.
If you want to append the killed region to the last killed text,
use C-M-w before C-
The copied text is filtered by ‘filter-buffer-substring’ before it is
saved in the kill ring, so the actual saved text might be different
from what was in the buffer.
When called from Lisp, save in the kill ring the stretch of text
between BEG and END, unless the optional argument REGION is
non-nil, in which case ignore BEG and END, and save the current
region instead.
This command is similar to ‘copy-region-as-kill’, except that it gives
visual feedback indicating the extent of the region being copied.
This function has :around advice: ‘ad-Advice-kill-ring-save’.
(fn BEG END &optional REGION)
Key | Saves at point |
---|---|
M-w w |
word |
M-w s |
sexp |
M-w l |
list |
M-w d |
defun |
M-w D |
defun name |
M-w f |
filename |
M-w b |
buffer file name or default directory |
There are also modifiers to treat how the saved text is handled.
Modifier | Effect |
---|---|
@ |
append to previous kill |
C-w |
kill selection |
+ , - , 1..9 |
expand/shrink selection |
0 |
shrink selection to initial size |
<SPC> |
cycle through easy-kill-alist |
C-<SPC> |
turn selection into active region |
C-g |
abort |
? |
help |
4.2.5. Vundo PENDING
I like to have undo navigation. Vundo gives a nice mini interface (git-style) to move around undo history.
(use-package vundo :bind ("C-x u" . vundo) :custom (vundo-glyph-alist vundo-unicode-symbols))
When in the undo mode, some keys help with navigation.
Key | Effect |
---|---|
f |
go forward |
b |
go backward |
n |
go to the node below at branch point |
p |
go to the node above |
a |
go back to last branch |
e |
go to the end of the branch |
l |
go to last saved node |
r |
go to next saved node |
m |
mark current node for diff |
u |
unmark marked node |
d |
show a diff |
q |
quit (C-g also works) |
4.2.6. Expand Region
Easily one of my favorite packages, this is a nice way to expand selections to semantic regions. Read more on https://github.com/magnars/expand-region.el.
(use-package expand-region :ensure t :bind ("C-=" . er/expand-region))
4.2.7. Citar (Bibtex completions)
For getting completions from bibliographic data, Citar links things together.
(use-package citar :commands (citar-capf-setup) :ensure t :bind (:map org-mode-map ("C-c b" . #'org-cite-insert)) :custom (citar-bibliography bnb/biblio) (org-cite-global-bibliography bnb/biblio) (org-cite-insert-processor 'citar) (org-cite-follow-processor 'citar) (org-cite-activate-processor 'citar) :hook (LaTeX-mode . citar-capf-setup) (org-mode . citar-capf-setup)) (use-package citar-embark :ensure t :after (citar embark) :no-require :config (citar-embark-mode) :custom (citar-at-point-function 'embark-act)) (use-package citar-org-roam :ensure t :after (citar org-roam) :config (add-to-list 'org-roam-capture-templates '("n" "Literature note" plain "%?" :target (file+head "%(expand-file-name (or citar-org-roam-subdir \"\") org-roam-directory)/${citar-citekey}.org" "#+title: ${citar-citekey} (${citar-date}). ${note-title}.\n#+created: %U\n#+last_modified: %U\n\n") :unnarrowed t)) (citar-org-roam-mode))
4.2.8. Tree Sitter
Introduced in emacs 29, tree sitter transforms code into a concrete syntax tree. Read up on how to get started with tree-sitter
4.3. Version Control
Emacs is fantastic for interfacing with version control systems. For git, it may have the best interface.
4.3.1. Magit
Magit is a git interface for Emacs.
Here I set a global key for magit-status
. Think 'G' looks like 6.
(use-package magit :ensure t :bind ("<f6>" . magit-status) :custom (magit-last-seen-setup-instructions "1.4.0"))
4.3.2. Smerge
Somewhere along the line, smerge
was added to native version
control support. To facilitate editing merge conflicts, this hydra
helps me do the work.
(use-package smerge :ensure nil :bind (:map smerge-mode-map ("C-c ^ h" . hydra-smerge/body)) :mode-hydra (hydra-smerge (:color amaranth :title " SMerge" :quit-key "q") ("Selection" (("a" smerge-keep-all "Keep all") ("b" smerge-keep-base "Keep base") ("m" smerge-keep-mine "Keep mine") ("o" smerge-keep-other "Keep other") ("r" smerge-resolve "Keep mine")) "Movement" (("n" smerge-next "Next conflict") ("p" smerge-previous "Previous conflict")))))
4.4. Checking
Authors can always use that little bit of extra help to ensure the prose is right from the beginning.
4.4.1. Writegood Mode
To avoid weaslewords, passive voice, and accidental duplicates, employ Writegood.
(use-package writegood-mode :ensure t :bind ("C-c g" . writegood-mode) ("C-c C-g g" . writegood-grade-level) ("C-c C-g e" . writegood-reading-ease))
4.4.2. Spell Checking
This site has an interesting suggestion on how to use aspell
for
CamelCase spell checking.
(elpaca nil (progn (cond ((executable-find "aspell") (setq ispell-program-name (executable-find "aspell") ispell-extra-args '("--sug-mode=ultra" "--lang=en_US"))) (t (setq ispell-program-name nil) (message "No aspell found!"))) (bind-key "H-$" 'ispell-word)))
4.4.3. Proselint
To get a complete, robust analysis of writing, Proselint can be configured to work as a checker for flycheck.
Note that the executable needs to be installed on the system and is not automatically provided.
(with-eval-after-load "flycheck-mode" (flycheck-define-checker proselint "A linter for prose" :command ("proselint" source-inplace) :error-patterns ((warning line-start (file-name) ":" line ":" column ": " (id (one-or-more (not (any " ")))) (message (one-or-more not-newline) (zero-or-more "\n" (any " ") (one-or-more not-newline))) line-end)) :modes (text-mode markdown-mode gfm-mode org-mode)) (add-to-list 'flycheck-checkers 'proselint))
4.5. Buffers
Handling buffers is central to an effective Emacs experience. This section adds in the tools to make management easy.
4.5.1. Midnight Mode
This mode looks at midnight and kills any inactive buffers (keeping things tidy). By default, inactive means is any buffer untouched for three days.
(use-package midnight :ensure nil :defer 10)
4.5.2. IBuffer
Use ibuffer
Begin using Ibuffer to edit a list of buffers.
Type
Uses keymap ‘ibuffer-mode-map’, which is not currently defined.
C-h m after entering ibuffer for more information.
All arguments are optional.
OTHER-WINDOW-P says to use another window.
NAME specifies the name of the buffer (defaults to "*Ibuffer*").
QUALIFIERS is an initial set of filtering qualifiers to use;
see ‘ibuffer-filtering-qualifiers’.
NOSELECT means don’t select the Ibuffer buffer.
SHRINK means shrink the buffer to minimal size. The special
value ‘onewindow’ means always use another window.
FILTER-GROUPS is an initial set of filtering groups to use;
see ‘ibuffer-filter-groups’.
FORMATS is the value to use for ‘ibuffer-formats’.
If specified, then the variable ‘ibuffer-formats’ will have
that value locally in this buffer.
(fn &optional OTHER-WINDOW-P NAME QUALIFIERS NOSELECT SHRINK FILTER-GROUPS FORMATS) instead of list-buffers
Display a list of existing buffers.
The list is displayed in a buffer named "*Buffer List*".
See ‘buffer-menu’ for a description of the Buffer Menu.
By default, all buffers are listed except those whose names start
with a space (which are for internal use). With prefix argument
ARG, show only buffers that are visiting files.
(fn &optional ARG) for buffer management. The
most visible difference being the coloring that ibuffer
uses.
I also squash any empty groups from being displayed and add hooks to automatically set the filter groups and update contents.
(use-package ibuffer :ensure nil :bind ("C-x C-b" . ibuffer) :custom (ibuffer-show-empty-filter-groups nil) :hook (ibuffer-mode . (lambda () (ibuffer-auto-mode 1) (ibuffer-switch-to-saved-filter-groups "Standard"))))
- Groups
The buffer list splits into arbitrary groups for easier management. Below I create an "Org" group for
org-mode
buffers.(setq ibuffer-saved-filter-groups '(("Standard" ("Emacs" (or (filename . ".*bnb-emacs.*") (mode . emacs-lisp-mode))) ("Org" (mode . org-mode)) ("Magit" (name . "\*magit")) ("Mail" (or (mode . message-mode) (mode . mail-mode))) ("HTML" (mode . html-mode)) ("Help" (or (name . "\*Help\*") (name . "\*Apropos\*") (name . "\*info\*"))))))
- VC Grouping
The ibuffer-vc Package provides groups according to version control sets. Here I setup a small keybinding (
/ v
) to get to the filtered vc groups. The keys/ R
will go back to the standard view.(use-package ibuffer-vc :ensure t :bind (:map ibuffer-mode-map ("/ ;" . ibuffer-vc-set-filter-groups-by-vc-root)))
- VC Grouping
4.5.3. Unique Buffer Names
When editing files with the same name, but different location, a
unique identifier (based on path) is preferred over a number. The
format below shows the buffername as <filename>:<parent directory>
.
(use-package uniquify :ensure nil :defer 10 :config (setq uniquify-buffer-name-style 'post-forward uniquify-separator ":"))
4.5.4. OSX Reveal
For file-backed buffers, reveal the file in OSX finder with this binding.
(use-package reveal-in-osx-finder :ensure t :bind ("C-c z" . reveal-in-osx-finder))
4.6. Development
The minor modes for development deal mainly with parenthenses and structured editing.
4.6.1. Check parens on save
This check has saved me from a broken configuration file many times. I highly recommend.
(add-hook 'after-save-hook 'check-parens nil t)
4.6.2. Eldoc
While developing, documentation is nice to have handy and automatic.
(use-package eldoc :ensure nil :hook (prog-mode . turn-on-eldoc-mode) (ielm-mode . turn-on-eldoc-mode) :custom (eldoc-documentation-strategy 'eldoc-documentation-compose-eagerly) :config (eldoc-add-command-completions "paredit-"))
4.6.3. Eglot
(use-package eglot :ensure nil)
4.6.4. Paredit
I added paredit-mode
to several of the lisp modes that follow.
(use-package paredit :ensure t :delight " 🍐" :hook (emacs-lisp-mode . paxedit-mode) (clojure-mode . paxedit-mode) :commands (paredit-mode))
4.6.5. Paxedit
Maybe even more power for lisp coding? Paxedit repo
(use-package paxedit :ensure t :delight " ꁀ" :hook (emacs-lisp-mode . paxedit-mode) (clojure-mode . paxedit-mode) :bind (:map paxedit-mode-map ("M-<right>" . paxedit-transpose-forward) ("M-<left>" . paxedit-transpose-backward) ("M-<up>" . paxedit-backward-up) ("M-<down>" . paxedit-backward-end) ("M-b" . paxedit-previous-symbol) ("M-f" . paxedit-next-symbol) ("C-%" . paxedit-copy) ("C-&" . paxedit-kill) ("C-*" . paxedit-delete) ("C-^" . paxedit-sexp-raise) ("M-u" . paxedit-symbol-change-case) ("C-@" . paxedit-symbol-copy) ("C-#" . paxedit-symbol-kill)))
4.6.6. Rainbow Delimiters
In deeply nested structures (I'm looking at you lisp), automatically coloring the matching delimiter can speed up understanding and refactoring.
For a lighter-weight alternative, check out show-paren-mode
Non-nil if Show-Paren mode is enabled.
See the ‘show-paren-mode’ command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node ‘Easy Customization’)
or call the function ‘show-paren-mode’..
(use-package rainbow-delimiters :ensure t :hook (prog-mode . rainbow-delimiters-mode))
4.7. Organization
There are a couple of built-in features that help with organization. (Outside of org-mode, that is)
4.7.1. Bookmarks
http://emacswiki.org/emacs/BookMarks
Keystroke | Action |
---|---|
C-x r m |
Set a bookmark |
C-x r b |
Jump to a bookmark |
C-x r l |
List your bookmarks |
M-x bookmark-delete |
Delete bookmark by name |
The settings below auto-save bookmarks, adds a fringe marker on the current line when setting/jumping, and confirms bookmark deletion.
(setq bookmark-save-flag t bookmark-set-fringe-mark t bookmark-menu-confirm-deletion t)
4.7.2. Project
The built-in emacs package project
replaces the projectile
functionality. Use C-x p p
to navigate to a project and get started.
Useful key mappings
Binding | Function |
---|---|
C-x p p |
Find project |
C-x p f |
Find file in project |
C-x p b |
Find buffer in project |
C-x p e |
Eshell in project |
5. Major Modes
This section holds the details for major modes. These are interfaces in their own right.
5.1. File Management
Finding files in the minibuffer is easy with the completion frameworks above, but Emacs also functions very well as a fully fledged file manager.
5.1.1. Dired
The built-in directory editor, dired
"Edit" directory DIRNAME--delete, rename, print, etc. some files in it.
Optional second argument SWITCHES specifies the options to be used
when invoking ‘insert-directory-program’, usually ‘ls’, which produces
the listing of the directory files and their attributes.
Interactively, a prefix argument will cause the command to prompt
for SWITCHES.
If DIRNAME is a string, Dired displays a list of files in DIRNAME (which
may also have shell wildcards appended to select certain files).
If DIRNAME is a cons, its first element is taken as the directory name
and the rest as an explicit list of files to make directory entries for.
In this case, SWITCHES are applied to each of the files separately, and
therefore switches that control the order of the files in the produced
listing have no effect.
You can flag files for deletion with d and then
delete them by typing x.
Type h after entering Dired for more info.
If DIRNAME is already in a Dired buffer, that buffer is used without refresh.
(fn DIRNAME &optional SWITCHES), can be customized to a great
degree. The following sections walk us through my particular
preferences.
- Basic Settings
First, add the basic dired settings.
(setq dired-kill-when-opening-new-dired-buffer t dired-mark-region t)
The first setting,
dired-kill-when-opening-new-dired-buffer
If non-nil, kill the current buffer when selecting a new directory. keepsdired
from littering little visited folder buffers along the way.Marking commands can now act on regions with
dired-mark-region
Defines what commands that mark files do with the active region.
When nil, marking commands don’t operate on all files in the
active region. They process their prefix arguments as usual.
When the value of this option is non-nil, then all Dired commands
that mark or unmark files will operate on all files in the region
if the region is active in Transient Mark mode.
When ‘file’, the region marking is based on the file name.
This means don’t mark the file if the end of the region is
before the file name displayed on the Dired line, so the file name
is visually outside the region. This behavior is consistent with
marking files without the region using the key m that advances
point to the next line after marking the file. Thus the number
of keys used to mark files is the same as the number of keys
used to select the region, for example M-2 m marks 2 files, and
C-SPC M-2 n m marks 2 files, and M-2 S-m marks 2 files. set to
When ‘line’, the region marking is based on Dired lines,
so include the file into marking if the end of the region
is anywhere on its Dired line, except the beginning of the line.t
. - Hacks
These are part of the dired-hacks repository.
(use-package dired-hacks-utils :defer t :ensure t)
- Filter mode
This first package provides
dired-filter-mode
Toggle filtering of files in Dired.
When you toggle the filter mode, the filter stack and all other
state is preserved, except the display is not altered. This
allows you to quickly toggle the active filter without need of
popping the stack and then re-inserting the filters again.
This is a minor mode. If called interactively, toggle the
‘Dired-Filter mode’ mode. If the prefix argument is positive,
enable the mode, and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is ‘toggle’. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate ‘dired-filter-mode’.
The mode’s hook is called both when the mode is enabled and when
it is disabled.
(fn &optional ARG) giving some very handy ways to filter the view. Enable it withC-c C-d f
and then get to the commands withC-c C-f
.(use-package dired-filter :defer t :hook (dired-mode . dired-filter-mode) :ensure t :bind ("C-c C-d f" . dired-filter-mode) :bind-keymap ("C-c C-f" . dired-filter-map))
- Colorized Files
This next addition colorizes files in dired accorting to type or the
chmod
bits.(use-package dired-rainbow :ensure t :config (progn (dired-rainbow-define html "#eb5286" ("css" "less" "sass" "scss" "htm" "html" "jhtm" "mht" "eml" "mustache" "xhtml")) (dired-rainbow-define xml "#f2d024" ("xml" "xsd" "xsl" "xslt" "wsdl" "bib" "json" "msg" "pgn" "rss" "yaml" "yml" "rdata")) (dired-rainbow-define document "#9561e2" ("docm" "doc" "docx" "odb" "odt" "pdb" "pdf" "ps" "rtf" "djvu" "epub" "odp" "ppt" "pptx")) (dired-rainbow-define markdown "#ffed4a" ("org" "etx" "info" "markdown" "md" "mkd" "nfo" "pod" "rst" "tex" "textfile" "txt")) (dired-rainbow-define database "#6574cd" ("xlsx" "xls" "csv" "accdb" "db" "mdb" "sqlite" "nc")) (dired-rainbow-define media "#de751f" ("mp3" "mp4" "MP3" "MP4" "avi" "mpeg" "mpg" "flv" "ogg" "mov" "mid" "midi" "wav" "aiff" "flac")) (dired-rainbow-define image "#f66d9b" ("tiff" "tif" "cdr" "gif" "ico" "jpeg" "jpg" "png" "psd" "eps" "svg")) (dired-rainbow-define log "#c17d11" ("log")) (dired-rainbow-define shell "#f6993f" ("awk" "bash" "bat" "sed" "sh" "zsh" "vim")) (dired-rainbow-define interpreted "#38c172" ("py" "ipynb" "rb" "pl" "t" "msql" "mysql" "pgsql" "sql" "r" "clj" "cljs" "scala" "js")) (dired-rainbow-define compiled "#4dc0b5" ("asm" "cl" "lisp" "el" "c" "h" "c++" "h++" "hpp" "hxx" "m" "cc" "cs" "cp" "cpp" "go" "f" "for" "ftn" "f90" "f95" "f03" "f08" "s" "rs" "hi" "hs" "pyc" ".java")) (dired-rainbow-define executable "#8cc4ff" ("exe" "msi")) (dired-rainbow-define compressed "#51d88a" ("7z" "zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar" "war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar")) (dired-rainbow-define packaged "#faad63" ("deb" "rpm" "apk" "jad" "jar" "cab" "pak" "pk3" "vdf" "vpk" "bsp")) (dired-rainbow-define encrypted "#ffed4a" ("gpg" "pgp" "asc" "bfe" "enc" "signature" "sig" "p12" "pem")) (dired-rainbow-define fonts "#6cb2eb" ("afm" "fon" "fnt" "pfb" "pfm" "ttf" "otf")) (dired-rainbow-define partition "#e3342f" ("dmg" "iso" "bin" "nrg" "qcow" "toast" "vcd" "vmdk" "bak")) (dired-rainbow-define vc "#0074d9" ("git" "gitignore" "gitattributes" "gitmodules")) (dired-rainbow-define-chmod executable-unix "#38c172" "-.*x.*") (dired-rainbow-define-chmod directory "#6cb2eb" "d.*")))
- Narrowing
Instead of filter mode, we can use two functions to narrow the
dired
buffer to only the matches. The flavors of filterring are fuzzy or regexp.(use-package dired-narrow :defer t :bind (:map dired-mode-map ("C-c C-d n" . dired-narrow-fuzzy) ("C-c C-d r" . dired-narrow-regexp)) :ensure t)
- Collapsing directories
In the case of deep folder structures without any intermediate files (files only in the leaves), this mode helps collapse the view to be easier to navigate.
Per the documentation, this will be especially useful in directories such as
~/.config/foo/config
.(use-package dired-collapse :defer t :bind ("C-c C-d c" . dired-collapse-mode) :ensure t)
- External Commands
Also, there is a nice faculty to run an external command on a given file with
=!=
.
- Filter mode
5.1.2. Neotree
Neotree is a file sidebar for navigation. Dired has a superior
interface for interacting with files, but Neotree
offers the tree
stucture view for the times you need to see the whole folder tree.
(use-package neotree :ensure t :commands (neotree) :bind ("H-t" . neotree-toggle) :config (setq neo-theme (if (display-graphic-p) 'icons 'arrow)))
5.2. Documents
These days, one of my favorite uses of the one true editor is to author prose and scientific writing. These major modes provide the tools to make this easy and fun.
5.2.1. AucTeX
Using TeX systems is easy with AUCTeX. To learn everything, read the AUCTeX Manual.
The following block ensures that AUCTeX builds and loads
correctly. The main settings deal with setting up PDF
output and
the xetex
engine.
(use-package auctex :defer t :after org :ensure (auctex :pre-build (("./autogen.sh") ("./configure" "--without-texmf-dir" "--with-lispdir=.") ("make"))) :mode (("\\.tex\\'" . LaTeX-mode) ("\\.tex\\.erb\\'" . LaTeX-mode) ("\\.etx\\'" . LaTeX-mode)) :hook ((LaTeX-mode . flyspell-mode) (LaTeX-mode . LaTeX-math-mode) (LaTeX-mode . auto-fill-mode) (LaTeX-mode . orgtbl-mode) (doc-view-mode . auto-revert-mode)) :config (setq TeX-auto-untabify t TeX-auto-save t TeX-save-query nil TeX-parse-self t TeX-output-view-style (if (eq system-type 'windows-nt) (quote (("^pdf$" "." "SumatraPDF.exe -reuse-instance %o") ("^html?$" "." "start %o"))) (quote (("^pdf$" "." "evince -f %o") ("^html?$" "." "start %o")))) TeX-command-extra-options "-shell-escape" TeX-PDF-mode 1 TeX-engine 'xetex) (setq-default TeX-master nil) (setq-default TeX-global-PDF-mode 1) (add-to-list 'org-latex-packages-alist '("" "tikz" t)) (add-to-list 'org-latex-packages-alist '("" "minted" t)) (setq org-latex-create-formula-image-program 'imagemagick) (eval-after-load "preview" '(add-to-list 'preview-default-preamble "\\PreviewEnvironment{tikzpicture}" t)))
Let me walk through some of the settings.
Parse-self and auto-save will parse the file on load and save respectively. Untabify will remove tabs (real ones) before saving.
I also have a default of TeX-master
set to nil
. I used to have it
set to "master" as recommended in the documentation, but I had bad
results for LaTeX files generated on the fly.
For viewing the output, I can specify the command to use on the files generated in the process. However, the programs differ on GNU/Linux and Windows, so I have per-platform settings.
There are two settings that add the tikz
and minted
packages to
org-mode
exports.
- RefTeX
RefTeX provides navigation, easy references, easy citations and integrates well into AUCTeX. Find all of the details in the RefTeX Manual.
Automatically turn it on in LaTeX modes.
(add-hook 'LaTeX-mode-hook 'turn-on-reftex)
Keystroke Function C-c = Show TOC and jump to sections C-c ( Insert a label C-c ) Reference a label C-c [ Insert a citation (from BibTex db) C-c < Index entry C-c > View index C-c & View crossref
5.2.2. HTML
The document standard that is ubiquitous, but typically authored through some processing tool. This section helps wtih the real hands-on editing of HTML (and other similarily structued) documents.
- Impatient mode
In order to play nicely with HTML+, it needs to be added to the filters of impatient mode.
(use-package impatient-mode :ensure t :mode "\\.html\\'" :bind ("C-x C-h s" . httpd-start) ("C-x C-h x" . httpd-stop) ("C-x C-h d" . httpd-serve-directory) :config (add-to-list 'imp-default-user-filters '(mhtml-mode . nil)))
To use impatient mode, you'll first want to call
httpd-start
Start the web server process. If the server is already
running, this will restart the server. There is only one server
instance per Emacs instance. and then navigate to http://localhost:8080/imp to see the rendered buffers. - Emmet
Emmet mode allows for terse description of nested elements. There is great documentation on the approach at emmet.io.
(use-package emmet-mode :ensure t :commands (emmet-mode) :hook ((web-mode . emmet-mode) (sgml-mode . emmet-mode) (css-mode . emmet-mode)))
5.2.3. Markdown
Everything can't be as nice as org-mode. Oh well.
(use-package markdown-mode :ensure t :commands (markdown-mode gfm-mode) :mode (("README\\.md\\'" . gfm-mode) ("\\.md\\'" . markdown-mode) ("\\.markdown\\'" . markdown-mode)) :init (setq markdown-command "multimarkdown"))
5.2.4. Orgmode
The one feature I cannot do without. Let's set up some basics.
(use-package org :ensure (org :type git :repo "https://git.savannah.gnu.org/git/emacs/org-mode.git" :tag "release_9.6.19") :delight (org-mode "🦄" :major) :mode ("\\.org\\(_archive\\)?\\'" . org-mode) :bind ("C-c t" . orgtbl-mode) ("C-c l" . org-store-link) ("C-c r" . org-capture) ("C-c a" . org-agenda) ("<f12>" . org-agenda) ("H-z" . org-agenda) ("<apps>" . org-agenda) ("<f9> g" . org-clock-goto) ("<f9> i" . org-clock-in) ("<f9> o" . org-clock-out) (:map org-mode-map ("M-i" . org-toggle-inline-images) ("C-c ," . org-insert-structure-template) ("C-c ," . org-insert-structure-template) ("M-o" . ace-link-org)) :hook (org-babel-after-execute . (lambda () (org-display-inline-images t t))))
This block installs org-mode along with some bindings.
Binding | Effect |
---|---|
C-c t |
Edit tables in any mode |
C-c l |
Store a link to thing at point |
C-c r |
Dispatch org capture |
<f12> |
Open org agenda |
H-z |
Open org agenda |
<apps> |
Open org agenda |
<f9> g |
Goto open org clock |
<f9> i |
Org clock in |
<f9> o |
Org clock out |
- Contrib
There are a set of contributed packages that install nicely together. This code block ensures that the libraries are available.
(use-package org-contrib :after org :ensure t)
- Auto mode
I add
_archive
to the list of known org files. This alternative extensions correctly identifies org archives (.org_archive
).This mode is set above with
use-package
. - Hooks
There are two hooks to consider. Theone initialized in 5.2.4 toggles on the inline images.
The next hook just saves the org files opened before exiting emacs – just in case.
(with-eval-after-load 'org (add-hook 'bnb/kill-emacs-hooks 'org-save-all-org-buffers 'append))
- Speed Keys
Using org-mode efficiently for task management is best done with speed keys. This are in effect when the cursor is on the first
*
of a headline. And they come with an easy cheat-sheet by typing?
. I enable this feature and add some of my own commands.(with-eval-after-load 'org (setq org-use-speed-commands t org-speed-commands (append org-speed-commands '(("BNB Additions") ("0" . delete-window) ("1" . delete-other-windows) ("2" . split-window-vertically) ("3" . split-window-horizontally) ("z" . org-add-note) ("h" . hide-other) ("." . org-save-all-org-buffers) ("/" . org-global-cycle) ("k" . #'(lambda () (org-mark-subtree) (kill-region (region-beginning) (region-end))))))))
- Org Configuration
This is the meat of what Org can do. Keeping track of todo items with due dates, tags, etc. is really powerful. And I get to customize it to suit my needs and my workflow.
- Keywords
The keywords that org uses in the headlines exist as sequences describing the state changes. The sequences describe how you can cycle through the different states. However, I don't cycle through states and just select them. I find that is better for the large list of possibilities here.
The characters in
()
also allow fast access to these states described here.(with-eval-after-load 'org (setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!/!)") (sequence "WAITING(w@/!)" "SOMEDAY(s!)" "|" "CANCELED(c@/!)") (sequence "CANCELED(c@/!)"))))
- Tags
Org uses tags on headlines for organization. I don't currently use them much. I organize mainly by file with a file tag specified via in-buffer settings (
#+FILETAGS
).However, a global tag list provides a selection list for the tagging interface. I use 'project' as my tag to easily differentiate simple tasks from more complex ones.
(with-eval-after-load 'org (setq org-tag-alist '(("PROJECT" . ?p))))
- Mechanics
The todo interface allows easy selection of states and triggers on certain states to store notes.
Instead of cycling through states (and possibly triggering log entries), I prefer fast entry to jump right to the correct state. I also turn off the S-cursor transitions as state changes to avoid the logging prompts.
(with-eval-after-load 'org (setq org-use-fast-todo-selection t org-treat-S-cursor-todo-selection-as-state-change nil))
Upon changing the state of todo items, I can automatically add/remove tags with the following list. It's a bit lispy, but describes what happens upon entry in the specified state. The state named as a string has tuples of tags and flags. 't' indicates to set the flag, empty means to remove it.
(with-eval-after-load 'org (setq org-todo-state-tags-triggers '(("CANCELED" ("CANCELED" . t)) ("WAITING" ("WAITING" . t)) ("SOMEDAY" ("SOMEDAY" . t)) (done ("WAITING")) ("TODO" ("WAITING") ("CANCELED")) ("NEXT" ("WAITING")) ("DONE" ("WAITING") ("CANCELED")))))
Along with tags and states are priorities. I do not use task priorities myself so I turn them off.
(with-eval-after-load 'org (setq org-enable-priority-commands nil))
- Logging
Org allows logging of states. I turn this on to prompt myself for reasons behind specific state changes. There is also a setting to set a different drawer for clocking and logs.
(with-eval-after-load 'org (setq org-log-done 'note org-log-redeadline 'time org-log-reschedule 'time org-log-into-drawer t org-drawers '("PROPERTIES" "LOGBOOK" "CLOCK")))
- Sub-tasks
Naturally, some tasks are projects composed of smaller sub-tasks. Org handles this easily. I like to enforce the dependencies of regular todo items and plain checkbox lists. In this way, the overall item cannot change to done without the completion of the sub-tasks.
(with-eval-after-load 'org (setq org-enforce-todo-checkbox-dependencies t org-enforce-todo-dependencies t))
Because of the previous enforcement of state, I can also automatically infer when a parent state is complete. The following code marks the parent complete once the sub-tasks are all done.
(with-eval-after-load 'org (defun org-summary-todo (n-done n-not-done) "Switch entry to DONE when all sub-entries are done, to TODO otherwise." (let (org-log-done org-log-states) (org-todo (if (= n-not-done 0) "DONE" "TODO")))) (add-hook 'org-after-todo-statistics-hook 'org-summary-todo))
- Logging
- Keywords
- Capture
Capturing is crucial to a task system and in this vein, org is no slouch. The capture templates define what get captured, where it goes, and what the user needs to type.
;; Files (setq org-default-notes-file "~/Documents/Org/Inbox.org" bnb/weekly-reports-file "~/Documents/Org/WeeklyReports.org") ;; Default templates (setq org-capture-templates `(("t" "Todo" entry (file ,org-default-notes-file) "* TODO %?\n %U\n%^{Score}p" :clock-in t :clock-resume t) ("r" "todo (Remember location)" entry (file org-default-notes-file) "* TODO %?\n %U\n %a" :clock-in t :clock-resume t) ("n" "Note" entry (file org-default-notes-file) "* %? :NOTE:\n %U\n %a\n :CLOCK:\n :END:") ("c" "Capture current TODO mix in table" table-line (file+headline ,bnb/weekly-reports-file "Burndown") "%(bnb/org-count-tasks-by-status)") ("s" "Capture Weekly Score in table" table-line (file+headline ,bnb/weekly-reports-file "Scores") "%(bnb/add-weekly-score-table-entry)") ("e" "Capture Weekly time in table" table-line (file+headline ,bnb/weekly-reports-file "Minutes") "%(bnb/org-time-logged-table-entry)") ("u" "Url" entry (file ,org-default-notes-file) "* TODO %?\n %U\n\n %(org-mac-chrome-get-frontmost-url)") ("m" "Mail" entry (file ,org-default-notes-file) "* TODO %?\n %U\n\n %(org-mac-message-get-links \"s\")")))
There are five main capture templates here. The first two store a todo item in my Refile.org file. The only difference is automatic (contextual) link storage in the second case.
The next item simply stores a note. The next for "Weekly Report" is a work in progress. I think that I'll have to either settle for a proper datetree or write a custom function.
The final item is not for direct use, but through the
org-protocol
interface andorg-outlook
usage. This lets me add a link to an Outlook message on windows. I can then get an email at work, mark it to store in emacs and quickly get back to the message later.- Capture-template helpers for data tables
These helpers provide functionality used in the capture templates above.
Modified from Sacha Chua, this code get the current mix of tasks in the agenda files. I use this as part of my weekly review for task amount and mix at a glance.
(defun bnb/org-count-tasks-by-status () "Create a table entry for the tracking of task mix." (interactive) (let ((counts (make-hash-table :test 'equal)) (today (format-time-string "%Y-%m-%d" (current-time))) values output) (org-map-entries (lambda () (let ((status (elt (org-heading-components) 2))) (when status (puthash status (1+ (or (gethash status counts) 0)) counts)))) "-HOME" 'agenda) (setq values (mapcar (lambda (x) (or (gethash x counts) 0)) '("DONE" "TODO" "WAITING" "CANCELLED" "SOMEDAY"))) (setq output (concat "| " today " | " (mapconcat 'number-to-string values " | ") " | " (number-to-string (apply '+ values)) " | " (number-to-string (round (/ (* 100.0 (car values)) (apply '+ values)))) "% |")) (if (called-interactively-p 'any) (insert output) output)))
I also have a helper function to get the score of done tasks closed within the last week. I store this in a table line with year and workweek number.
(defun bnb/add-weekly-score-table-entry () "Track my weekly scores in a table." (let ((score (apply '+ (org-map-entries (lambda () (string-to-number (or (org-entry-get (point) "Score") "0"))) "/DONE" 'agenda))) (year (format-time-string "%Y" (current-time))) (ww (number-to-string (bnb/workweek)))) (format "| %s | %s | %s |" year ww score)))
How about the hours logged last week? Let's give that a go. Note that if there are no clocked hours for a week, this comes up empty.
(defun bnb/org-time-logged-table-entry (&optional additional-weeks include-zeros) "Insert table of minutes per category. Optionally provide ADDITIONAL-WEEKS to get more history" (interactive "P") (unless additional-weeks (setq additional-weeks 0)) (let* ((minh (make-hash-table :test 'equal)) (now (decode-time)) (start (encode-time 0 0 0 (- (nth 3 now) (nth 6 now) (* 7 (or additional-weeks 1))) (nth 4 now) (nth 5 now))) (today (format-time-string "%Y-%m-%d" (current-time)))) ;; Collect minutes clocked per category (org-map-entries (lambda () (let ((category (org-entry-get-with-inheritance "CATEGORY" t)) (minutes (org-clock-sum-current-item start))) (puthash category (+ minutes (or (gethash category minh) 0)) minh))) t 'agenda) ;; Print out table lines (let ((rows nil)) (maphash (lambda (k v) (when (or (> v 0) include-zeros) (setq rows (cons (format "| %s | %s | %d |" today k v) rows)))) minh) (if (called-interactively-p 'any) (insert (mapconcat 'identity rows "\n")) (mapconcat 'identity rows "\n"))))) ; (bnb/org-time-logged-table-entry nil t)
- Capture-template helpers for data tables
- Refile
Refiling notes is also spectacular with Org. That is what makes it possible for me to simply put every captured item into Refile.org and worry about organization later.
For my setup, I use separate files that hold a singular Tasks headline. Because of that, I turn on caching first.
For the refile targets, I will allow up to 2 levels of search for filing in any of the agenda files. For refiling within the current file, I set the max to five levels. Anything deeper than six levels will exhaust the depth of my thought.
The refiling system is set to
confirm
creation of parent nodes. This allows me to not only refile, but create along the way for faster organization.Finally, I set the filenames to be first for refiling.
(setq org-refile-use-cache t org-refile-targets '((org-agenda-files :maxlevel . 2) (nil :maxlevel . 5)) org-refile-allow-creating-parent-nodes 'confirm org-refile-use-outline-path 'file)
- Agenda
Once I have captured and refiled my tasks, I need to remember to do them and see what is on the agenda. The ways to view the tasks at hand are nicely programmable.
Some basic settings control small tidbits in the agenda. I turn on tags in the agenda line, show the logged items for the day, and only show a time grid if a scheduled tasks exists.
(setq org-agenda-show-inherited-tags t org-agenda-log-mode-items '(clock) org-agenda-clockreport-parameter-plist '(:link nil :maxlevel 2 :fileskip0 t) org-agenda-block-separator nil org-agenda-dim-blocked-tasks nil org-agenda-inhibit-startup t org-agenda-breadcrumbs-separator " ❱ ")
- Super agenda
Enriching the agenda view is easy with org-super-agenda. I don't make any default settings here, but override
org-super-agenda-groups
List of groups to apply to agenda views.
See readme for information. for each agenda view dispatcher.(use-package org-super-agenda :after org :ensure t :config (org-super-agenda-mode))
The examples online provide a good idea of the grouping customization (and ease).
- Category Icons
Org-mode can show category icons in some agenda views. The underlying setting is just an
alist
of categories and the icons to use.;;;;;;;;;;;;; ;; Org agenda category icons (with-eval-after-load 'nerd-icons (setq org-agenda-category-icon-alist `(("[iI]nbox" ;; ,(list (nerd-icons-octicon "nf-oct-inbox")) nil nil :ascent center) ("[Ww]ork" ;; ,(list (nerd-icons-octicon "nf-oct-organization")) nil nil :ascent center) ("[Pp]ersonal" ;; ,(list (nerd-icons-octicon "nf-oct-home")) nil nil :ascent center) ("[Ii][Cc]" ;; ,(list (nerd-icons-octicon "nf-oct-person")) nil nil :ascent center) ("[Mm]anager" ;; ,(list (nerd-icons-octicon "nf-oct-ruby")) nil nil :ascent center))))
- Views
The key to exploring open work are the agenda views. These provide a landscape to list, filter or manipulate tasks.
org-agenda-custom-commands
Custom commands for the agenda.
These commands will be offered on the splash screen displayed by the
agenda dispatcher ‘C-c a’. Each entry is a list like this:
(key desc type match settings files)
key The key (one or more characters as a string) to be associated
with the command.
desc A description of the command. When omitted or nil, a default
description is built using MATCH.
type The command type, any of the following symbols:
agenda The daily/weekly agenda.
agenda* Appointments for current week/day.
todo Entries with a specific TODO keyword, in all agenda files.
search Entries containing search words entry or headline.
tags Tags/Property/TODO match in all agenda files.
tags-todo Tags/P/T match in all agenda files, TODO entries only.
todo-tree Sparse tree of specific TODO keyword in *current* file.
tags-tree Sparse tree with all tags matches in *current* file.
occur-tree Occur sparse tree for *current* file.
alltodo The global TODO list.
stuck Stuck projects.
... A user-defined function.
match What to search for:
- a single keyword for TODO keyword searches
- a tags/property/todo match expression for searches
- a word search expression for text searches.
- a regular expression for occur searches
For all other commands, this should be the empty string.
settings A list of option settings, similar to that in a let form, so like
this: ((opt1 val1) (opt2 val2) ...). The values will be
evaluated at the moment of execution, so quote them when needed.
files A list of files to write the produced agenda buffer to with
the command ‘org-store-agenda-views’.
If a file name ends in ".html", an HTML version of the buffer
is written out. If it ends in ".ps", a PostScript version is
produced. Otherwise, only the plain text is written to the file.
You can also define a set of commands, to create a composite agenda buffer.
In this case, an entry looks like this:
(key desc (cmd1 cmd2 ...) general-settings-for-whole-set files)
where
desc A description string to be displayed in the dispatcher menu.
cmd An agenda command, similar to the above. However, tree commands
are not allowed. Valid commands for a set are:
(agenda "" settings)
(agenda* "" settings)
(alltodo "" settings)
(stuck "" settings)
(todo "match" settings files)
(search "match" settings files)
(tags "match" settings files)
(tags-todo "match" settings files)
Each command can carry a list of options, and another set of options can be
given for the whole set of commands. Individual command options take
precedence over the general options.
When using several characters as key to a command, the first characters
are prefix commands. For the dispatcher to display useful information, you
should provide a description for the prefix, like
(setq org-agenda-custom-commands
'(("h" . "HOME + Name tag searches") ; describe prefix "h"
("hl" tags "+HOME+Lisa")
("hp" tags "+HOME+Peter")
("hk" tags "+HOME+Kim")))
See also Info node ‘(org) Custom Agenda Views’. defines which views are available by default.Adapted from org-leuven-agenda-views.txt.
;; Reset everything to nil (setq org-agenda-custom-commands nil)
(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("o" "My Agenda" ((todo "TODO" ((org-agenda-overriding-header "\n⚡ Do Today\n┄┄┄┄┄┄┄┄┄┄") (org-agenda-remove-tags t) (org-agenda-prefix-format " %-2i %-15b") (org-agenda-todo-keyword-format ""))) (agenda "" ((org-agenda-start-day "+0d") (org-agenda-span 5) (org-agenda-overriding-header "⚡ Schedule\n┄┄┄┄┄┄┄┄┄┄") (org-agenda-repeating-timestamp-show-all nil) (org-agenda-remove-tags t) (org-agenda-prefix-format " %-3i %-15b %t%s") (org-agenda-todo-keyword-format " ☐ ") (org-agenda-current-time-string "⮜┈┈┈┈┈┈┈ now") (org-agenda-scheduled-leaders '("" "")) (org-agenda-time-grid (quote ((daily today remove-match) (0900 1200 1500 1800 2100) " " "┈┈┈┈┈┈┈┈┈┈┈┈┈")))))))))
- Process
In the process section, the goal is to take the incoming items and process them. There is a menu key,
p
, that will open up the different views.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("p" . "PROCESS...") t))
The first view grabs anything tagged
inbox
. In my setup, my default notes file does have a filetag ofinbox
, so all of those will also show up in this view.The nice part is that I can have other agenda files with inboxes that need addressed This helps them stay in the forefront of processing.
(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("pp" "All TODOs in Inboxen" ((org-ql-block '(and (tags "inbox") (or (todo) (done))) ((org-ql-block-header "Inboxed tasks") (org-agenda-overriding-header "List of all TODO tasks in Inbox") (org-agenda-sorting-strategy '(priority-down)) (org-super-agenda-groups '((:auto-category t))))))) t))
I try to put effort estimates on all of my tasks. As part of processing, the
pe
view lists tasks without effort. Typically I then edit withorg-columns
Turn on column view on an Org mode file.
Column view applies to the whole buffer if point is before the first
headline. Otherwise, it applies to the first ancestor setting
"COLUMNS" property. If there is none, it defaults to the current
headline. With a ‘C-u’ prefix argument, GLOBAL,
turn on column view for the whole buffer unconditionally.
When COLUMNS-FMT-STRING is non-nil, use it as the column format.
(fn &optional GLOBAL COLUMNS-FMT-STRING) (C-c C-x C-c
).In the column view, navigating to the effort column and hitting the right number will set the effort to one of the predefined settings (found in
org-global-properties
List of property/value pairs that can be inherited by any entry.
This list will be combined with the constant ‘org-global-properties-fixed’.
The entries in this list are cons cells where the car is a property
name and cdr is a string with the value.
Buffer local properties are added either by a document property drawer
:PROPERTIES:
:NAME: VALUE
:END:
or by adding lines like
#+PROPERTY: NAME VALUE).(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("pe" "No Estimates" ((org-ql-block '(not (effort)) ((org-agenda-overriding-header "Lacking ESTIMATES") (org-agenda-sorting-strategy '(priority-down)) (org-super-agenda-groups '((:auto-category t))))))) t))
This view (reachable via
ps
) is similar to the previous one, only it finds tasks without scores. This helps me process them in the same way – ensure everthing that can have a score, does.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("ps" "No Score" ((org-ql-block '(not (property "SCORE")) ((org-agenda-overriding-header "Lacking SCORE") (org-agenda-sorting-strategy '(priority-down)))))) t))
Finally, this view shows the items tagged
someday
. As process goes, this could be part of review, but I like it better as a processing step.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("pd" "Somday items" tags-todo "SOMEDAY" ((org-agenda-overriding-header "SOMEDAY tasks") (org-agenda-sorting-strategy '(priority-down)))) t))
- Focus
The focus section builds up views to help focus on tasks. The dispatcher starts with an
f
.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("f" . "FOCUS...") t))
The today focus view unfolds like a compass guiding through the day's tasks, presenting a constellation of sections aimed at pinpointing the most crucial endeavors to tackle next.
In the calendar section, a mosaic of diary entries and timestamps for today awaits exploration, offering a glimpse into the rhythm of the day.
The inbox section beckons with its array of unscheduled top-level items, each a potential gem awaiting processing. Here lie tasks in need of scheduling, refining, or simply bringing to completion.
For those with deadlines looming, the due today and overdue sections serve as a stark reminder, listing tasks teetering on the edge of time. They demand attention, beckoning to be brought to fruition.
Meanwhile, the scheduled section offers a glimpse into the near future, showcasing the tasks slated for today, like eager performers awaiting their cue.
Lastly, the completed section stands as a testament to progress, a growing archive of triumphs marked by tasks conquered. As the week unfolds, this section burgeons, reflecting the journey traversed and milestones achieved.
(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands `("f." "Today" ((agenda "" ((org-agenda-entry-types '(:timestamp :sexp)) (org-agenda-overriding-header (concat "CALENDAR Today " (format-time-string "%a %d" (current-time)))) (org-agenda-span 'day))) (tags-todo "LEVEL=1+inbox" ((org-agenda-overriding-header "INBOX (Unscheduled)"))) (tags-todo "DEADLINE<\"<+1d>\"+DEADLINE>\"<-1d>\"" ((org-agenda-overriding-header "DUE TODAY") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)) (org-agenda-sorting-strategy '(priority-down)))) (tags-todo "DEADLINE<\"<today>\"" ((org-agenda-overriding-header "OVERDUE") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)) (org-agenda-sorting-strategy '(priority-down)))) (agenda "" ((org-agenda-entry-types '(:scheduled)) (org-agenda-overriding-header "SCHEDULED") (org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)) (org-agenda-sorting-strategy '(priority-down time-down)) (org-agenda-span 'day) (org-agenda-start-on-weekday nil))) (todo "DONE" ((org-agenda-overriding-header "COMPLETED")))) ((org-agenda-format-date "") (org-agenda-start-with-clockreport-mode nil))) t))
The hotlist keeps a focus on tasks that may be heating up. This has three sections: overdue tasks, ones due within a week, and anything marked FLAGGED.
(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("fh" "Hotlist" ((tags-todo "DEADLINE<\"<+0d>\"" ((org-agenda-overriding-header "OVERDUE"))) (tags-todo "DEADLINE>=\"<+0d>\"+DEADLINE<=\"<+1w>\"" ((org-agenda-overriding-header "DUE IN NEXT 7 DAYS"))) (tags-todo "DEADLINE=\"\"+FLAGGED|DEADLINE>\"<+1w>\"+FLAGGED" ((org-agenda-overriding-header "FLAGGED")))) ((org-agenda-todo-ignore-scheduled 'future) (org-agenda-sorting-strategy '(deadline-up)))) t))
Finally the Hot & Fast view assembles tasks into three sections: overdue, take less then 1 hour, or a score of 1.
(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("ff" "Hot & Fast" ((tags-todo "DEADLINE<\"<+0d>\"" ((org-agenda-overriding-header "OVERDUE"))) (tags-todo "Effort={.}+Effort<\"1:00\"" ((org-agenda-overriding-header "QUICK"))) (tags-todo "SCORE=1" ((org-agenda-overriding-header "EASY"))))) t))
- Review
After processing incoming items and focusing on some work, there is the step of reviewing to ensure that progress is being made in the right spots.
The set of views start on the
r
key.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("r" . "REVIEW...") t))
On a weekly basis, the view (
rw
) prvides a look back and look forward for all tasks.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("rw" "Weekly review" ((tags "{INBOX}&LEVEL<=2" ((org-agenda-overriding-header "NEW TASKS"))) (agenda "" ((org-agenda-clockreport-mode t) (org-agenda-format-date (concat "\n" "%Y-%m-%d" " %a " (make-string (window-width) ?_))) (org-agenda-overriding-header "PAST WEEK") (org-agenda-prefix-format " %?-11t %i %-12:c% s") (org-agenda-show-log 'clockcheck) (org-agenda-span 7) (org-agenda-start-day "-1w") (org-deadline-warning-days 0))) (agenda "" ((org-agenda-overriding-header "NEXT MONTH") (org-agenda-span 'month) (org-agenda-start-day "+0d") (org-deadline-warning-days 0))) (todo "PROJECT" ((org-agenda-overriding-header "PROJECT LIST"))) (todo "DONE|PROJECTDONE" ((org-agenda-overriding-header "Candidates to be archived"))))) t))
- All Tasks
The first review section is for all tasks and launchable via
ra
.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("ra" . "All Tasks...") t))
The first view lists tasks grouped by due dates. The sections of this view list tasks that are overdue, due today, due tomorrow, due within a week, due within a month, due later, waiting for, and tasks without a due date.
(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("rad" "All Tasks (grouped by Due Date)" ((tags-todo "DEADLINE<\"<+0d>\"" ((org-agenda-overriding-header "OVERDUE") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)))) (tags-todo "DEADLINE=\"<+0d>\"" ((org-agenda-overriding-header "DUE TODAY") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)))) (tags-todo "DEADLINE=\"<+1d>\"" ((org-agenda-overriding-header "DUE TOMORROW") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)))) (tags-todo "DEADLINE>\"<+1d>\"+DEADLINE<=\"<+7d>\"" ((org-agenda-overriding-header "DUE WITHIN A WEEK") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)))) (tags-todo "DEADLINE>\"<+7d>\"+DEADLINE<=\"<+28d>\"" ((org-agenda-overriding-header "DUE WITHIN A MONTH") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)))) (tags-todo "DEADLINE>\"<+28d>\"" ((org-agenda-overriding-header "DUE LATER") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)))) (tags-todo "TODO={WAIT}" ((org-agenda-overriding-header "WAITING FOR") (org-agenda-skip-function '(org-agenda-skip-entry-if 'deadline)))) (todo "" ((org-agenda-overriding-header "NO DUE DATE") (org-agenda-skip-function '(org-agenda-skip-entry-if 'deadline))))) ((org-agenda-sorting-strategy '(priority-down)) (org-agenda-write-buffer-name "All Tasks (grouped by Due Date)")) "~/Documents/Org/all-tasks-by-due-date.pdf") t))
Hitting
ra1
lists all tasks (with a due date) by due date.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("ra1" "All Tasks with a due date" alltodo "" ((org-agenda-overriding-header "All Tasks (sorted by Due Date)") (org-agenda-skip-function '(org-agenda-skip-entry-if 'notdeadline)) (org-agenda-sorting-strategy '(deadline-up)))) t))
(defconst bnb/org-completed-date-regexp (concat " \\(" "CLOSED: \\[%Y-%m-%d" "\\|" "- State \"\\(DONE\\)\" * from .* \\[%Y-%m-%d" "\\|" "- State .* -> *\"\\(DONE\\)\" * \\[%Y-%m-%d" "\\) ") "Matches any completion time stamp.") (defun current-time-ndays-ago (n) "Return the current time minus N days." (time-subtract (current-time) (days-to-time n))) (with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands `("rac" "Completed view" ((todo "TODO|DONE" ((org-agenda-overriding-header (format "YESTERDAY [%s]" (format-time-string "%a %d" (current-time-ndays-ago 1)))) (org-agenda-skip-function '(org-agenda-skip-entry-if 'notregexp (format-time-string bnb/org-completed-date-regexp (current-time-ndays-ago 1)))) (org-agenda-sorting-strategy '(priority-down)))) (todo "TODO|DONE" ((org-agenda-overriding-header (format "2 DAYS AGO [%s]" (format-time-string "%a %d" (current-time-ndays-ago 2)))) (org-agenda-skip-function '(org-agenda-skip-entry-if 'notregexp (format-time-string bnb/org-completed-date-regexp (current-time-ndays-ago 2)))) (org-agenda-sorting-strategy '(priority-down)))) (todo "TODO|DONE" ((org-agenda-overriding-header (format "3 DAYS AGO [%s]" (format-time-string "%a %d" (current-time-ndays-ago 3)))) (org-agenda-skip-function '(org-agenda-skip-entry-if 'notregexp (format-time-string bnb/org-completed-date-regexp (current-time-ndays-ago 3)))) (org-agenda-sorting-strategy '(priority-down))))) ((org-agenda-format-date "") (org-agenda-start-with-clockreport-mode nil)))))
- Timesheets
As part of a review, timesheets (reachable at
rt
) illustrate where the time went.(with-eval-after-load 'org (add-to-list 'org-agenda-custom-commands '("rt" . "Timesheet...") t))
The first timesheet is a daily one that includes the logged tasks and a clockreport.
(with-eval-after-load 'org ;; Show what happened today. (add-to-list 'org-agenda-custom-commands '("rtd" "Daily Timesheet" agenda "" ((org-agenda-log-mode-items '(clock closed)) (org-agenda-overriding-header "DAY TIMESHEET") (org-agenda-show-log '(clock closed clockcheck)) (org-agenda-span 'day) (org-agenda-start-with-clockreport-mode t) (org-agenda-start-with-log-mode t) (org-agenda-time-grid nil))) t))
Naturally, the level up from a daily timesheet is a weekly timesheet (reachable via
rtw
).(with-eval-after-load 'org ;; Show what happened this week. (add-to-list 'org-agenda-custom-commands '("rtw" "Weekly Timesheet" agenda "" ( ;; (org-agenda-format-date "") (org-agenda-overriding-header "WEEKLY TIMESHEET") (org-agenda-skip-function '(org-agenda-skip-entry-if 'timestamp)) (org-agenda-span 'week) (org-agenda-log-mode-items '(closed clock)) (org-agenda-show-log '(closed clock)) (org-agenda-start-on-weekday 1) (org-agenda-start-with-clockreport-mode t) (org-agenda-start-with-log-mode t) (org-agenda-time-grid '(weekly)))) t))
- All Tasks
- Process
- Super agenda
- Export
THe following sections configue global export settings that make sense for HTML and \LaTeX.
- HTML
For HTML, I just want to inline the links to images.
(setq org-export-html-inline-images t)
I suppress the postamble with
org-html-postamble
Non-nil means insert a postamble in HTML export.
When set to ‘auto’, check against the
‘org-export-with-author/email/creator/date’ variables to set the
content of the postamble. When t, insert a string as defined by the
formatting string in ‘org-html-postamble-format’. When set to a
string, use this formatting string instead (see
‘org-html-postamble-format’ for an example of such a formatting
string).
When set to a function, apply this function and insert the
returned string. The function takes the property list of export
options as its only argument.
Setting :html-postamble in publishing projects will take
precedence over this variable..(setq org-html-postamble nil)
I'll use the fancy HTML5 export by default.
(setq org-html-doctype "html5" org-html-html5-fancy t)
Striped tables are nice in email, but this is terribly difficult due to cruddy CSS support. Luckily, org-html-table-row-tags saves the day and assigns the right classes to the table rows with a little bit of help. This makes styling correct in CSS-reduced instances.
(with-eval-after-load 'org (setq org-html-table-row-tags (cons '(cond (top-row-p "<tr class=\"tr-top\">") (bottom-row-p "<tr class=\"tr-bottom\">") (t (if (= (mod row-number 2) 1) "<tr class=\"tr-odd\">" "<tr class=\"tr-even\">"))) "</tr>")))
- LaTeX
For \LaTeX, I want to convert fragments to images, and use minted for any source blocks. I also want to have XeTeX as the backend.
(setq org-export-latex-listings 'minted org-export-latex-custom-lang-environments '((emacs-lisp "common-lispcode")) org-export-latex-minted-options '() org-highlight-latex-and-related '(latex script entities) org-latex-to-pdf-process '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f" "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f" "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
- Tufte
Other contributed org-mode exporters require activiation to show up in the export menus. This exporter stylizes
html
in a beautiful way.(use-package ox-tufte :after org-mode :ensure t)
- Github Flavored Markdown
- Slack
This exporter is not added to the export dispatcher, but provides functions to copy the text into the clipboard or open a buffer with the markdown.
(use-package ox-slack :after (ox-gfm org-mode) :bind (:map org-mode-map ("C-c e c" . org-slack-export-to-clipboard-as-slack) ("C-c e b" . org-slack-export-as-slack)) :ensure t)
- HTML
- Clocking
I have found clocking to be useful in understanding where my time goes. And Org makes this easy, fast, and painless to do.
The clock has some general settings around persistence (resuming clocks), history length and resuming a task after clocking in twice (interrupted task).
(elpaca nil (org-clock-persistence-insinuate) (setq org-clock-history-length 28 org-clock-in-resume t))
Behavior of the clock can change to accommodate other needs. I like having clocks log into a specific drawer. Also, it is nice to remove zero-time clocks and clock out automatically when an item completes.
(setq org-clock-into-drawer "CLOCK" org-clock-out-remove-zero-time-clocks t org-clock-out-when-done t)
Two settings help resolve most clock issues that I have seen. Persisting the clock across sessions helps prevent loss of time by accident. Auto-resolution of open clocks help prompt how to handle the situation where a dangling clock exists.
(setq org-clock-persist 'history org-clock-auto-clock-resolution 'when-no-clock-is-running)
Two final settings regarding clocking configure how I change and view the clocks. I want any clock reports to include the currently clocked task as well. And for clock editing, I change to 15 minute increments.
(setq org-clock-report-include-clocking-task t org-time-stamp-rounding-minutes '(1 15))
- Modules
Org-modules allow for specific functionality within org-mode. These modules (plugins) activate by adding them to the list of
org-modules
Modules that should always be loaded together with org.el.
If a description starts with, the file is not part of Emacs and Org mode, .
so loading it will require that you have properly installed org-contrib
package from NonGNU Emacs Lisp Package Archive
https://elpa.nongnu.org/nongnu/org-contrib.html
You can also use this system to load external packages (i.e. neither Org
core modules, nor org-contrib modules). Just add symbols
to the end of the list. If the package is called org-xyz.el, then you need
to add the symbol ‘xyz’, and the package must have a call to:
(provide 'org-xyz)
For export specific modules, see also ‘org-export-backends’.(with-eval-after-load 'org (mapc (lambda (x) (add-to-list 'org-modules x t)) '(org-id org-habit org-plot org-protocol ol-bookmark)))
- Global Ids
By enabling the
org-id
module,org
will use globally unique identifiers for entries. - Habit
Some tasks repeat, but you still want to log when you have done it. I use this to help me always do my weekly or yearly reviews. By including it in
org-modules
, habits get activated.My one setting blow sets a width for the graph in Agenda View.
(setq org-habit-graph-column 50)
To view habits in the agenda (not just on today), try
C-u K
.To ensure that a task is treated as a habit, the
STYLE
property will have to be set tohabit
. Read more in the manual. - Org protocol
External applications can have actions in
org-mode
through the use of protocols. A popular usage is accepting bookmarks from browsers. Read more about it in the manual. - Bookmarks
The org linking system is powerful in the way it can connect to many part of Emacs. This module enables links to any stored bookmark.
- Global Ids
- Babel
Babel is some serious magic for Org files. It enables tangled code blocks (this file), notebook-style interactive documents, delarative diagrams, and nicely-formatted examples.
Each code block can have a different language and be edited remotely in a properly mode'd buffer.
This is the list of languages I like to have enabled for use.
(elpaca nil (org-babel-do-load-languages 'org-babel-load-languages '((calc . t) (C . t) (ditaa . t) (dot . t) (emacs-lisp . t) (gnuplot . t) (latex . t) (maxima . t) (perl . t) (plantuml . t) (python . t) (ruby . t) (shell . t) (sqlite . t) (sql . t) (R . t))))
- Extra Babel Packages
These two packages assist with making HTTP requets. They can act like an alternative to Postman.
(use-package ob-http :after org-mode :ensure t) (use-package ob-restclient :after org-mode :ensure (ob-restclient :host github :repo "alf/ob-restclient.el") :config (org-babel-do-load-languages 'org-babel-load-languages (append org-babel-load-languages '((restclient . t)))))
- Customization
Python needs to find the right executable, so I hard code what babel should use for a command.
(setq org-babel-python-command "python3")
- Extra Babel Packages
- Miscellaneous Settings
This section holds important small preferences for Org.
- Auto-revert mode
If the org files are under DVCS like git, then the edits may happen while open in emacs.
This is a global setting, but most useful for the org files that exists elsewhere.
(use-package autorevert :ensure nil :defer 10 :diminish " " :custom (global-auto-revert-mode t))
- Automatically save org files
I like to save early and often. In earlier versions of orgmode, I sometimes had the capture buffer/timer crash on me. So, now I save at the top of every hour to be sure.
(with-eval-after-load 'org (run-at-time "00:59" 3600 'org-save-all-org-buffers))
- Columns
The default columns are as follows. In English, it shows the item (or task), the score, the effort estimate, and the current clock sum (time already spent).
(setq org-columns-default-format "%80ITEM(Task) %5Score{+} %10Effort(Effort){:} %10CLOCKSUM")
- Display settings
There are a collection of settings that define how the headlines, subtrees, and notes render.
For the headline stars, there are two settings of note. I am explicit that I do not want only odd levels. I also like to hide the leading stars.
(setq org-odd-levels-only nil org-hide-leading-stars nil)
Cycling the headline states can produce different views of the files. I like this to be as compact as possible, so I try to squash the lines between the collapsed trees. There is also a flag to open a file collapsed. This I like too – I get a compact view of the file and can jump to a relevant section with
C-c C-j
.(setq org-cycle-separator-lines 0 org-startup-folded 'content)
When using SRC-blocks, org can provide highlighting native to the SRC type. Note that this may slow down some files.
(setq org-src-fontify-natively t)
- File Applications
This list lets org know how to handle the links of given file types. Most things open inside
emacs
, but the others set to default rely on the OS to supply a program.(setq org-file-apps '((auto-mode . emacs) ("\\.x?html?\\'" . default) ("\\.pdf\\'" . default) ("\\.mm\\'" . default)))
- Goto Interface
By using
C-c C-j
, you can jump easily around a large orgfile such as this one. Naturally, the interface you use to do so is customizable.I explicitly set it to the default because I sometimes go back and forth with the default and
outline-path-completion
setting.(setq org-goto-interface 'outline-path)
- IDO Integration
IDO integrates well into orgmode. Anytime completion is necessary, I like to use the IDO mechanics.
The
outline-path-completion
may conflict with IDO, so then it is best to have it not use IDO in this case.(setq org-completion-use-ido t org-outline-path-complete-in-steps nil)
- Insertion
I define when org should leave a blank line before an item. In my case it is headings and plain list items.
(setq org-blank-before-new-entry '((heading) (plain-list-item)))
Also, when inserting a new heading, do so after the current subtree.
(setq org-insert-heading-respect-content t)
Set the indentation to the outline node level.
(setq org-adapt-indentation t)
- Plantuml
Setup the path for orgmode to find the jar needed.
(setq org-plantuml-jar-path "/usr/local/Cellar/plantuml/1.2017.18/libexec/plantuml.jar")
- Properties
Here I add my global properties. The appendix
_ALL
indicates to the system that these are the values avaialble for completion for each of these properties.(setq org-global-properties '(("STYLE_ALL" . "habit") ("Effort_ALL" . "0:10 0:30 1:00 2:00 3:00 4:00") ("Score_ALL" . "1 2 3 5 8")))
- Special Control Keys
Org has a different idea of some of the default emacs commands to make it easier to work with the structures involved.
For
C-a
orC-e
within a headline, it will only try to navigate the headline text the first time. Additional keypresses will move to the true beginning/ending of lines.C-k
also can behave specially in headlines depending on its location. When point is at the beginning, it will kill the headline and the folded subtree below. In the middle of a headline, it kills the headline text up to the tags. After the headline text, it kills the tags.(setq org-special-ctrl-a/e t org-special-ctrl-k t)
- Auto-revert mode
- Pretty Org-mode
This collection of settings enhances the visual appeal when working in org-mode.
First, some initial built-in settings to make.This block styles the headlines to hide the stars, and fontify them. The next setting hides emphasis markers on words. And finally, entities are drawn with UTF-8 characters.
(setq org-hide-leading-stars t org-fontify-done-headline t org-hide-emphasis-markers t org-pretty-entities t)
Using Prettify, I add these symbols to make the structure of org-mode files cleaner to read. In the list, the strings on the left are replaced by the elements on the right. Moving a cursor over the symbols will show them in their original form.
(bnb/prettify "org-mode" '(("#+BEGIN_SRC" . "⌈") ("#+END_SRC" . "⌊") ("#+begin_src" . "⌈") ("#+end_src" . "⌊") (">=" . "≥") ("=>" . "⇨")))
- Org Bullets
Bullet shapes do a better job conveying depth then just position/depth of stars. This snippet loads the package and sets the list of bullets to use.
(use-package org-bullets :ensure t :custom (org-bullets-bullet-list '("◉" "◊" "○" "⧫" "✸" "⬨" "⬟" "⬧" "⬢" "⬫" "⌑" "⬪" "▱")) (org-ellipsis "˯") ;; Options: ˯⇂↯⤵🠻🢗 :hook (org-mode . org-bullets-mode))
- Org Bullets
- Org-AI
As much as I find this to be ridiculous, interacting with AI via Emacs is likely the one true way. Org-ai provides a natrual interface for real humans.
(use-package org-ai :ensure t :commands (org-ai-mode org-ai-global-mode) :init (org-ai-global-mode) ; installs global keybindings on C-c M-a :custom (org-ai-default-chat-model "gpt-3.5-turbo") :config (org-ai-install-yasnippets))
This adds the ability to have
#+begin/end_ai
blocks to have a "conversation" with ChatGPT or DALL⋅E. Be sure to setorg-ai-openai-api-token
This is your OpenAI API token.
You need to specify if you do not store the token in
‘auth-sources’. You can retrieve it at
https://platform.openai.com/account/api-keys. to your API token before using. - Org Repo Todo PENDING
Make it easy to setup a
TODO.org
from within a repo. I use this to capture thoughts while coding.(use-package org-repo-todo :ensure t :bind (("s-;" . ort/capture-todo) ("s-'" . ort/capture-checkitem) ("s-`" . ort/goto-todos)))
- Org Ref
Setups the Org-style interface with Biblio.
(use-package org-ref :defer 10 :config (setq org-ref-notes-directory "~/Documents/Personal/Org/Biblio/" org-ref-bibliography-notes "~/Documents/Personal/Org/Biblio/index.org" org-ref-default-bibliography '("~/Documents/Personal/Org/Biblio/index.bib") org-ref-pdf-directory "~/Documents/Personal/Org/Biblio/lib"))
- Org Noter
Taking notes in a PDF is a useful trick.
org-noter
lets me do just that.(use-package org-noter :ensure t :bind ("H-n" . org-noter))
- Org Roam
The best of all worlds? Org-mode and a Zettelkasten system? Yes, Org-roam sets up a knowledge capture and organization system built on the principles of Zettelkasten.
(use-package org-roam :ensure t :after org :delight " 𝕫" :hook (after-init . org-roam-setup) :init (setq org-roam-v2-ack t) :custom (org-roam-directory (file-truename "~/Documents/Org/zettel/")) :bind (("C-c n f" . org-roam-node-find) ("C-c n r" . org-roam-node-random) ("C-c n g" . org-roam-graph) ("C-c n c" . org-roam-capture) ("C-c n j" . org-roam-dailies-capture-today) (:map org-mode-map (("C-c n l" . org-roam-buffer-toggle) ("C-c n a" . org-roam-alias-add) ("C-c n i" . org-roam-node-insert) ("C-c n I" . org-roam-insert-immediate))) (:map org-roam-mode-map (("C-c n l" . org-roam) ("C-c n t" . org-roam-dailies-find-today) ("C-c n w" . org-roam-dailies-find-tomorrow) ("C-c n d" . org-roam-date) ("C-c n f" . org-roam-find-file) ("C-c n g" . org-roam-show-graph)))))
- Org QL PENDING
The Org Query Language helps walk through a builder interface to find, filter, and sort org entries.
(use-package org-ql :after org :commands (org-ql-search org-ql-view-sidebar) :bind ("C-c q s" . org-ql-search) ("C-c q b" . org-ql-view-sidebar) ("C-c q v" . org-ql-view) ("C-c q t" . org-ql-sparse-tree) ("C-c q r" . org-ql-view-recent-items) (:map org-mode-map ("C-c q f" . org-ql-find)) :ensure t :demand t)
This package is built on a library of searching functions, so it also has some neat tricks like easily specifying agenda views and building code blocks.
For example, to get a link to a dynamic list of pending items (in this file), click on this:
tags:PENDING
Org QL search links only work in Emacs..
5.2.5. PDF Tools
This replaces the built-in DocView for PDF files. Find out the details on the wiki.
The part that I like over DocView is the ability to add
annotations. They all live on C-c C-a
.
Key | Action |
---|---|
C-c C-a D |
delete annotation |
C-c C-a a |
attachment dired |
C-c C-a h |
add highlight |
C-c C-a l |
list annotations |
C-c C-a m |
add markup |
C-c C-a o |
add strikeout annotation |
C-c C-a s |
add squiggly annotation |
C-c C-a t |
add text annotation |
C-c C-a u |
add underline annotation |
(defcustom bnb/homebrew-prefix "" "Prefix to use for an alternative path to homebrew items" :type 'string :group 'bnb) (setenv "PKG_CONFIG_PATH" (let ((hbp bnb/homebrew-prefix)) (setq pdf-info-epdfinfo-program (concat hbp "/usr/local/bin/epdfinfo")) (mapconcat 'identity (list (concat hbp "/usr/local/Cellar/libffi/3.2.1/lib/pkgconfig") (concat hbp "/usr/local/Cellar/zlib/1.2.8/lib/pkgconfig") (concat hbp "/usr/local/lib/pkgconfig") "/opt/X11/lib/pkgconfig") ":"))) (use-package pdf-tools :defer t :ensure t :config (custom-set-variables '(pdf-tools-handle-upgrades nil)) (setq-default pdf-view-display-size 'fit-page) (pdf-loader-install))
5.3. Miscellaneous
This sections holds configuration details for some of the uncategorized major modes.
5.3.1. Calc
Working in computer land, I add these additional units to calc
.
(use-package calc :ensure nil :commands (calc) :init (setq math-additional-units '((GiB "1024 * MiB" "Giga Byte") (MiB "1024 * KiB" "Mega Byte") (KiB "1024 * B" "Kilo Byte") (B nil "Byte") (Gib "1024 * Mib" "Giga bit") (Mib "1024 * Kib" "Mega bit") (Kib "1024 * b" "Kilo bit") (b nil "bit") )))
5.3.2. Dashboard
Instead of the normal "splash" screen, show a dashboard instead.
(use-package dashboard :ensure t :hook (elpaca-after-init . #'dashboard-insert-startupify-lists) (elpaca-after-init . #'dashboard-initialize) :custom (dashboard-set-file-icons t) (dashboard-icon-type 'nerd-icons) (dashboard-set-heading-icons t) (dashboard-projects-backend 'project-el) (dashboard-items '((recents . 5) (agenda . 10) (projects . 5) (bookmarks . 10) (registers . 10))) :config (dashboard-modify-heading-icons '((recents . "nf-oct-pin") ;; (agenda . "nf-oct-goal") ;; (projects . "nf-oct-project") ;; (bookmarks . "nf-oct-bookmark") ;; (registers . "nf-oct-rows"))) ;; (dashboard-setup-startup-hook))
5.3.3. Hyperbole PENDING
GNU Hyperbole enriches buffers with links like hypertext. Try C-h
h
for the binding. And M-RET
for links (explicit and implicit).
(use-package hyperbole :ensure t :config (hyperbole-mode 1))
5.4. Programming Languages
Hardly needing an introduction, this section holds the modes and settings related to authoring in, and connecting to my favorite (and necessary) programming languages and interpreters.
5.4.1. C++
I don't need much extra for C++ support, but the following snippet makes compilation nicer.
- Compilation Buffers
I forget where I snarfed this from, but it does a great job fixing the ANSI escape sequences in compilation buffers.
(use-package ansi-color :ensure nil :hook ((compliation-filter . colorize-compilation-buffer)) :config (defun colorize-compilation-buffer () (toggle-read-only) (ansi-color-apply-on-region compilation-filter-start (point)) (toggle-read-only)))
5.4.2. Elisp
When modified emacs-lisp
, it is most helpful to use paredit,
paxedit, and eldoc.
(use-package elisp-mode :ensure nil :after (major-mode-hydra) :mode-hydra (emacs-lisp-mode (:title "Elisp Commands") ("Eval" (("b" eval-buffer "buffer") ("e" eval-defun "defun") ("r" eval-region "region") ("d" edebug-defun "edebug-defun")) "REPL" (("I" ielm "ielm")) "Test" (("t" ert "prompt") ("T" (ert t) "all") ("F" (ert :failed) "failed")) "Doc" (("." describe-foo-at-point "thing-at-pt") ("f" describe-function "function") ("v" descrive-variable "variable") ("i" info-lookup-symbol "info lookup")))) :hook (emacs-lisp-mode . paredit-mode) (emacs-lisp-mode . paxedit-mode) (emacs-lisp-mode . turn-on-eldoc-mode))
When programming in elisp
, a cheat sheet of functions can be very
useful. New to Emacs 28, shortdoc
Pop to a buffer with short documentation summary for functions in GROUP.
If FUNCTION is non-nil, place point on the entry for FUNCTION (if any).
If SAME-WINDOW, don’t pop to a new window.
(fn GROUP &optional FUNCTION SAME-WINDOW) provides that useful, quick,
navigatable list of documentation.
5.4.3. Javascript
For javascript, the only global setting is to shorten the indentation level to 2.
(setq js-indent-level 2)
5.4.4. Plantuml
Not a programming language, but certainly a language. I typically use this in Org document blocks to generate diagrams. The reference guide is available online.
(use-package plantuml-mode :ensure t)
5.4.5. Python
(bnb/prettify "python-mode" '(;; Syntax ("def" . ?ℱ) ("not" . ?❗) ("in" . ?∈) ("not in" . ?∉) ("return" . ?⟼) ("yield" . ?⟻) ("for" . ?∀) ;; Base Types ("int" . ?ℤ) ("float" . ?ℝ) ("str" . ?𝕊) ("True" . ?𝕋) ("False" . ?𝔽) ;; Mypy ("Dict" . ?𝔇) ("List" . ?ℒ) ("Tuple" . ?⨂) ("Set" . ?Ω) ("Iterable" . ?𝔊) ("Any" . ?❔) ("Union" . ?∪)))
5.4.6. Rust
(use-package rust-mode :ensure t :custom (rust-mode-treesitter-derive t))
(use-package flycheck-rust :after rust-mode :hook (flycheck-mode-hook . flycheck-rust-setup))
(use-package toml-mode :ensure (toml-mode :host github :repo "dryman/toml-mode.el") :mode "\\.toml\\'")
I also snuck in TOML support for the Cargo.toml files.
5.4.7. Emacs Speaks Statistics (ESS)
For my data crunching tasks, I use R and ESS (emacs speaks statistics) provides an interface (repl) for developing analysis and charts.
(use-package ess :ensure t :commands (R) :custom (ess-default-style 'Rstudio-) :init (require 'ess-site))
5.4.8. Utilities
These utilities make the programming experience easier across many languages.
- Flycheck
Similar to spellchecking, Flycheck performs on-the-fly syntax checking. I don't have it globally on, but easily available through
H-!
.(use-package flycheck :ensure t ;:init (global-flycheck-mode) :init (pretty-hydra-define hydra-flycheck (:color amaranth :quit-key "q" :title " FlyCheck") ("Action" (("c" flycheck-mode "Buffer flycheck") ("g" global-flycheck-mode "Global flycheck mode")) "Errors" (("n" flycheck-next-error "Next error") ("p" flycheck-previous-error "Previous error") ("l" flycheck-list-errors "List errors") ("C" flycheck-clear-errors "Clear errors")) "Explanation" (("x" flycheck-explain-error-at-point "Explain error") ("h" flycheck-display-error-at-point "Dieplay error") ("s" flycheck-select-checker "Select checker") ("?" flycheck-describe-checker "Describe checker")))) :bind ("H-!" . hydra-flycheck/body))
- Prog fill
To go from wide lines to vertical, prog-fill comes into the rescue.
(use-package prog-fill :ensure t :commands (prog-fill) :hook (prog-mode . (lambda () (local-set-key (kbd "M-q") #'prog-fill))) :bind ("H-q" . prog-fill) :custom (prog-fill-floating-close-paren-p nil) (prog-fill-break-method-immediate-p t))
- Smart Tabs
SmartTabs try to do the right thing regarding tabs/spaces in indentation/alignment. It installs through the package interface. Look for
smart-tabs-mode
.By default, I'm enabling it in all modes that I can.
Since we are dealing with tabs here, I also take the time to set the default width to 4. Because of the way this mode works, any change in the default width will result in code that still aligns.
(use-package smart-tabs-mode :defer 10 :ensure t :init (setq-default indent-tabs-mode nil) (smart-tabs-insinuate 'c 'cperl 'c++))
- Treesit
Now built into Emacs 29, Tree-Sitter is a new parsing library. Read more about it online. This code sets up the library for Emacs 29 and greater, and installs come common grammars.
(use-package treesit :ensure nil ;; built-in :if (version<= "29" emacs-version) :init (defun bnb/treesit-setup-install-grammars () "Install Tree-sitter grammars if they are absent." (interactive) (dolist (grammar '((css . ("https://github.com/tree-sitter/tree-sitter-css" "v0.20.0")) (html . ("https://github.com/tree-sitter/tree-sitter-html" "v0.20.1")) (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript" "v0.20.1" "src")) (json . ("https://github.com/tree-sitter/tree-sitter-json" "v0.20.2")) (python . ("https://github.com/tree-sitter/tree-sitter-python" "v0.20.4")) (toml . ("https://github.com/tree-sitter/tree-sitter-toml" "v0.5.1")) (yaml . ("https://github.com/ikatyang/tree-sitter-yaml" "v0.5.0")))) (add-to-list 'treesit-language-source-alist grammar) ;; Only install `grammar' if we don't already have it ;; installed. However, if you want to *update* a grammar then ;; this obviously prevents that from happening. (unless (treesit-language-available-p (car grammar)) (treesit-install-language-grammar (car grammar))))) ;; Optional, but recommended. Tree-sitter enabled major modes are ;; distinct from their ordinary counterparts. ;; ;; You can remap major modes with `major-mode-remap-alist'. Note ;; that this does *not* extend to hooks! Make sure you migrate them ;; also (dolist (mapping '((python-mode . python-ts-mode) (css-mode . css-ts-mode) (js2-mode . js-ts-mode) (bash-mode . bash-ts-mode) (css-mode . css-ts-mode) (json-mode . json-ts-mode) (js-json-mode . json-ts-mode))) (add-to-list 'major-mode-remap-alist mapping)) :config (bnb/treesit-setup-install-grammars))
- Combobulate PENDING
Enable structured editing and movement with Combobulate.
(use-package combobulate :after treesit :if (version<= "29" emacs-version) :ensure (combobulate :host github :repo "mickeynp/combobulate") :custom ;; You can customize Combobulate's key prefix here. ;; Note that you may have to restart Emacs for this to take effect! (combobulate-key-prefix "C-c o") ;; Optional, but recommended. ;; ;; You can manually enable Combobulate with `M-x ;; combobulate-mode'. :hook ((python-ts-mode . combobulate-mode) (js-ts-mode . combobulate-mode) (html-ts-mode . combobulate-mode) (css-ts-mode . combobulate-mode) (yaml-ts-mode . combobulate-mode) (json-ts-mode . combobulate-mode)))
5.4.9. Web Mode
For all of the webish-stuff, this mode works well. Let's enable it on the right things.
(use-package web-mode :defer :mode "\\.html\\'" :ensure t :bind ("H-b" . browse-url-of-file) :custom (web-mode-markup-indent-offset 2) (web-mode-css-indent-offset 2) (web-mode-code-indent-offset 2) (web-mode-engines-alist '(("handlebars" . "\\.hbs\\'"))) :config (add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . web-mode)) (add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode)) (add-to-list 'auto-mode-alist '("\\.hbs\\'" . web-mode)) (add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode)) (add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode)) (add-to-list 'auto-mode-alist '("\\.svelte\\'" . web-mode)))
5.5. Shells
There are two useful shells in emacs: eshell
and ansi-term
.
5.5.1. Ansi Term
These settings close ansi-term
when I exit the shell. They also
default to just launching zsh
instead of asking me
(preferred). Then it closes by setting up a hook to ensure some
nice functionality in the terminal mode window.
(defun bnb/term-mode-hook () "Setup `term-mode`." (goto-address-mode) (setq-local term-buffer-maximum-size 10000)) (use-package term :ensure nil :hook (term-mode . bnb/term-mode-hook) :init (defalias 'zsh 'ansi-term) :custom (ansi-term-color-vector [term term-color-black term-color-red term-color-green term-color-yellow term-color-blue term-color-magenta term-color-cyan term-color-white]) (ansi-color-faces-vector [default bold shadow italic underline bold bold-italic bold]))
My hook above makes it easy to click on links in any output and protects the buffer from runaway printing.
5.5.2. Eshell
Built-in Eshell can provide a shell that works the same on windows or GNU/Linux. One of the really cool features is that you can define commands to use (like aliases) within the shell and have them directly integrate with emacs.
- Eshell Settings
Turn off any
$PAGER
settings inherited in the environment. Because this is running in Emacs, there is no need for a pager.(setenv "PAGER" "cat") (defun bnb/setup-eshell () "Setup functions to run in the eshell hook" (eshell/alias "em" "emacs") (eshell/alias "ll" "ls -Aloh") (pcase system-type ('darwin (eshell/alias "llc" "*ls -AlohG")) (- (eshell/alias "llc" "*ls -AlohG --color=always")))) (use-package eshell :ensure nil :commands eshell :hook (eshell-mode . bnb/setup-eshell))
- Eshell Commands
Like many shells,
eshell
allows for the definition of custom commands. The following settings add extra functionality for command-line use.Fast fingers are used to typing
emacs
at a prompt to open a file. This gives the same behavior ineshell
.(defun eshell/emacs (&rest args) "Open a file in emacs the natural way" (if (null args) ;; If emacs is called by itself, then just go to emacs directly (bury-buffer) ;; If opening multiple files with a directory name, e.g. ;; > emacs bar/bar.txt foo.txt ;; then the names must be expanded to complete file paths. ;; Otherwise, find-file will look in the current directory which ;; would fail for 'foo.txt' in the example above. (mapc #'find-file (mapcar #'expand-file-name (eshell-flatten-list (reverse args))))))
One can also keep the shell active and open files in the other window. (Mnemonic here is emacs other window)
(defun eshell/emo (&rest args) (mapc (lambda (f) (save-selected-window (find-file-other-window f))) (mapcar #'expand-file-name (eshell-flatten-list (reverse args)))))
On a windows box, setup grep to be a cygwin version.
(when (eq system-type 'windows-nt) (with-eval-after-load "eshell" (defun eshell/grep (&rest args) (eshell-grep "c:/cygwin/bin/grep.exe" args t))))
For Magit, there are some niceties to add.
(defun eshell/gst (&rest args) (magit-status-setup-buffer (pop args)) (eshell/echo)) (defun eshell/gd (&rest args) (magit-diff-unstaged) (eshell/echo)) (defun eshell/gds (&rest args) (magit-diff-staged) (eshell/echo))
- Plan 9 Smart Shells
See the complete guide to mastering Eshell for more on this. Basically, the cursor stays on the command for editing if necessary.
(elpaca nil (with-eval-after-load 'eshell (require 'eshell) (require 'em-smart) (setq eshell-where-to-jump 'begin eshell-review-quick-commands nil eshell-smart-space-goes-to-end t) (add-hook 'eshell-mode-hook 'eshell-smart-initialize)))
5.6. Visual
This section contains settings and packages relevant to visual elements. The theming of emacs is in the style section.
5.6.1. Nerd Icons
The set of nerd icons depends on an installed compatible font, but there are many alternatives. Because it is font-based, this will work graphically and in the terminal.
Be sure to run nerd-icons-install-fonts for this to work well.
(use-package nerd-icons :ensure t)
- Icons in Corfu
This package adds icons to th Corfu completion list.
(use-package nerd-icons-corfu :after corfu :ensure t :config (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
- Icons in dired
- Icons in Ibuffer
- Icons in completion
Use icons in the completion systems.
(use-package nerd-icons-completion :ensure t :after marginalia :hook (marginalia-mode . nerd-icons-completion-marginalia-setup) :init (nerd-icons-completion-mode))
6. Style
The following sections describe items that affect the visual elements of Emacs.
6.1. Frame Changes
These following items make Emacs really beautiful on every platform. I remove the menu bar, tool bar and the scroll bar for starters.
;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early. (push '(menu-bar-lines . 0) default-frame-alist) (push '(tool-bar-lines . 0) default-frame-alist) (push '(vertical-scroll-bars) default-frame-alist)
6.2. Window Changes
In the fringe area, I like to have markers to show me where the buffer begins/ends on the right. On the left, I have emacs show little dashes where empty lines exist.
In the title bar, I have it print the buffer name, full file name and size.
(setq-default indicate-buffer-boundaries 'right) (setq-default indicate-empty-lines t) (setq-default frame-title-format '("%b %f %I"))
6.3. Faces
Emacs faces are text styled with attributes including font, slant, weight, color, etc. These sections go over the necessary settings to make them exactly right in all circumstances.
For configuration and debugging, list-faces-display
List all faces, using the same sample text in each.
The sample text is a string that comes from the variable
‘list-faces-sample-text’.
If REGEXP is non-nil, list only those faces with names matching
this regular expression. When called interactively with a prefix
argument, prompt for a regular expression using ‘read-regexp’.
(fn &optional REGEXP) is useful.
6.3.1. Default Fonts
(elpaca nil (when window-system (defun bnb/filter-existing-fonts (fl) "Filter the list for only existing fonts" (seq-filter #'font-info fl)) (setq bnb/fontlist '("-*-Lunar Mono-*-expanded-*-12" "-*-Lunar Mono-*-*-*-12" "Fira Code-13" "Source Code Pro-13") bnb/font-ring (ring-convert-sequence-to-ring (seq-filter #'font-info bnb/fontlist)) bnb/font (ring-ref bnb/font-ring 0)) (defun bnb/font-apply (font) "Change the default font to FONT." (set-frame-font (setq bnb/font font)) (message "Set default font to %s" bnb/font)) (defun bnb/font-next () "Cycle the default font to the next in the ring." (interactive) (bnb/font-apply (ring-next bnb/font-ring bnb/font))) (defun bnb/font-prev () "Cycle the default font to the previous in the ring." (interactive) (bnb/font-apply (ring-prev bnb/font-ring bnb/font))) (set-frame-font bnb/font) (bind-keys ("H-f" . bnb/font-next) ("H-F" . bnb/font-prev))))
6.3.2. Unicode PENDING
Support unicode fonts throughout the system. Be sure to install recommended fonts for support.
(use-package unicode-fonts :disabled t :ensure t :defer 10 :init (unicode-fonts-setup))
6.3.3. Trying out fonts on Windows
On Windows, this function shows a font selection screen and sets the default face. This will not persist between sessions, but is great for test driving.
(defun bnb/windows-set-font () "Use Windows font selection to set the default font." (interactive) (set-face-attribute 'default nil :font (w32-select-font)))
6.3.4. Dynamic Font sizes
Changing font sizes in presentations is crucial to have at hand. I
use the following keybindings. C--
overrides the negative argument
function, but that one is also accessible by M--
.
(defun bnb/change-frame-font-size (fn) "Change the frame font size according to function FN." (let* ((font-name (frame-parameter nil 'font)) (decomposed-font-name (x-decompose-font-name font-name)) (font-size (string-to-number (aref decomposed-font-name 5)))) (aset decomposed-font-name 5 (int-to-string (funcall fn font-size))) (set-frame-font (x-compose-font-name decomposed-font-name)))) (defun bnb/frame-text-scale-increase () "Increase the frame font size by 1." (interactive) (bnb/change-frame-font-size '1+)) (defun bnb/frame-text-scale-decrease () "Decrease the frame font size by 1." (interactive) (bnb/change-frame-font-size '1-)) (elpaca nil (bind-keys ("C-+" . text-scale-increase) ("C--" . text-scale-decrease) ("s--" . bnb/frame-text-scale-decrease) ("s-+" . bnb/frame-text-scale-increase) ("s-=" . bnb/frame-text-scale-increase)))
6.3.5. Mode Line Style
I dislike the box around the mode-line
making it look like a
button. I disable (set to nil
) the box
face attribute to get a
flat feel. Be sure to do it to all mode-line
faces that have this
attribute.
(set-face-attribute 'mode-line nil :box nil) (set-face-attribute 'mode-line-inactive nil :box nil) (set-face-attribute 'mode-line-highlight nil :box nil)
6.3.6. Missing Glyphs
If I ever use a font with a missing glyph, this will let Emacs check the Symbola font for the missing data.
Download Symbola if you do not have it.
(set-fontset-font "fontset-default" nil (font-spec :size 20 :name "Symbola"))
6.3.7. Cursor width
Make the cursor the full width of the character at point.
(setq x-stretch-cursor t)
6.4. Themes
This function ensures that all enabled themes are shut down – not just the most current one.
(defun bnb/disable-all-themes () "Disable all enabled themes." (interactive) (mapc #'disable-theme custom-enabled-themes))
On creating themes: https://www.gnu.org/software/emacs/manual/html_node/emacs/Creating-Custom-Themes.html#Creating-Custom-Themes
6.4.1. Extra Themes
I like to have a few options for themes easily available. This set respresents my favorite go-to combinations.
(use-package minimal-theme :ensure t :defer t) (use-package gruvbox-theme :ensure t :defer t) (use-package material-theme :ensure t :defer t) (use-package tango-plus-theme :ensure t :defer t) (use-package color-theme-sanityinc-tomorrow :ensure t :defer t) ;; dichromacy ;; adwaita
6.5. Sky Color Clock
This is a nice addition to any modeline. In a little block, it shows the date, time, moon phase
(use-package sky-color-clock :ensure (sky-color-clock :host github :repo "zk-phi/sky-color-clock") :commands (sky-color-clock) :custom (sky-color-clock-enable-emoji-icon t) :config (sky-color-clock-initialize 40) (sky-color-clock-initialize-openweathermap-client bnb/openweathermap-api-key bnb/openweathermap-city-id))
The variables containging my city and API key are stored in local customizations.
6.6. Doom Modeline
Keeping with the goal of a clean and tidy Emacs, Doom modeline provides that fancy, fast, and minimal design.
I only customize the height to hug the font and keep everythig small.
(use-package doom-modeline :ensure t :init (doom-modeline-mode t) :custom (doom-modeline-height 0))
6.7. Asterism (⁂) Mode PENDING
Let's try ⁂-mode for an interesting mini-buffer line. It writes information into the minibuffer. Here I have it set to show the output of sky color clock.
(use-package asterism-mode :after sky-color-clock :ensure (asterism-mode :host gitlab :repo "lunar.studio/asterism-mode") :init (defun bnb/smaller-sky-color () (let* ((scc (sky-color-clock)) (len (length scc))) (add-face-text-property 0 len '(:height 90) t scc) ;(add-face-text-property 0 len '(:justification right) t scc) scc)) :config (setq ⁂-format '((:eval (bnb/smaller-sky-color)))))
6.8. Prettify Symbols
Emacs can be so pretty sometimes. The modes that follow are great for mathematical notation in Emacs.
The built-in prettify-symbols-mode
Non-nil if Prettify-Symbols mode is enabled.
Use the command ‘prettify-symbols-mode’ to change this variable. is easy to use. Check out what
it does (and how it can be adjusted) in prettify-symbols-alist
Alist of symbol prettifications.
Each element looks like (SYMBOL . CHARACTER), where the symbol
matching SYMBOL (a string, not a regexp) will be shown as
CHARACTER instead.
CHARACTER can be a character, or it can be a list or vector, in
which case it will be used to compose the new symbol as per the
third argument of ‘compose-region’..
I have additional python settings to make it look sharp.
(setq global-prettify-symbols-mode t prettify-symbols-unprettify-at-point 'right-edge)
Some resources for additional customization:
6.9. Color
Having nice colors can help understand status, types, symbols, or even nesting.
6.9.1. Rainbow Mode
In order to see the colors in the buffer this mode higlights color words and definitions with their values.
(use-package rainbow-mode :commands (rainbow-mode) :ensure t)
6.9.2. KureColor
Color helper KureColor allows easy modifications of hue, saturation and brightness.
(use-package kurecolor :bind (("H-k" . kurecolor-increase-hue-by-step) ("H-j" . kurecolor-decrease-hue-by-step) ("s-k" . kurecolor-increase-saturation-by-step) ("s-j" . kurecolor-decrease-saturation-by-step) ("s-l" . kurecolor-increase-brightness-by-step) ("s-h" . kurecolor-decrease-brightness-by-step)) :ensure t)
7. Postamble
These elements are at the end to keep all customizations tidy.
7.1. Local customizations (custom.el)
I typically use the customize interface to generate any local settings such as proxies, paths, fonts, etc. that may vary from machine to machine.
(setq custom-file "~/.emacs.d/custom.el") (load-file custom-file)
7.2. Local customizations (user-login-name)
I also intend to have a generic call to an installed local file
that may need to behave differently from custom.el
. This loads
last so that it can modify any existing setting made here to work
on the specific system in question.
In the code below, I add ~/.emacs.d/
to the load path and have a
protected call to load-library
. If the file exists, it gets
loaded, otherwise the error normally returned if the file is
non-existent gets ignored.
(elpaca nil (condition-case err (progn (load-file (format "~/.emacs.d/%s.el" user-login-name)) (message "Loaded local settings file %s.el" user-login-name)) (file-error (message "Skipping %s library as it does not exist." user-login-name)) nil))
7.3. Housekeeping
This message prints when Elpaca loading is complete
(message "README EVALUATION DURATION: %3.2f seconds" (- (float-time) bnb/start-time)) (elpaca nil (message "ELPACA LOADING COMPLETE: %s" (emacs-init-time)))
The local variables here setup a save hook (just in this buffer) that will create full.el for loading.
I used to use Org to tangle on the fly during initialization, but this method supports a pre-tangling that speeds up initialization.