This Emacs configuration is intended for TTY-only, with minimal and optional external program dependencies.
This configuration assumes Emacs version ~27~ or newer.
Clone this repository and initialize and update the submodules (see “Cloning a Project with Submodules” under Submodules in Pro Git).
$ git clone https://github.com/matheuristic/emacs-config.git $ cd emacs-config
Make sure GNU Stow is installed. Run the following to create the required directory structure in the user home directory and symlink the individual files.
$ stow -t $HOME --no-folding emacs-26-nox
This configuration uses several external tools, which need to be
compiled, installed or configured. Search for headings with an
external
tag in this Org file, and follow the instructions there.
Note that Emacs versions 27
and newer also support XDG Base
Directory Specification, so it is possible to use ~/.config/emacs
as
an alternative emacs directory. If desired, use the follow GNU Stow
command instead of the one above.
$ mkdir -p $HOME/.config/emacs $ stow -d ./emacs -t $HOME/.config/emacs --no-folding .emacs.d
The Emacs Lisp (Elisp) config files for Emacs can be generated with
M-x org-babel-tangle
or C-c C-v C-t
while in this file’s buffer.
The following files will be created in the emacs-26-nox/.emacs.d
directory:
init.el
: Main configuration file.
elpa
: Uses a package from GNU ELPA.external
: Configuration code in the section uses external tools that are not typically packaged with the default userland on Linux and BSD systems (including macOS), and which need to be installed at the system-level outside of the Emacspackage.el
mechanisms. Also includes Elisp scripts that need to be downloaded manually.transient
: Enables, creates or modifies Transient definitions.melpa
: Uses a package from MELPA.semiearly
: Configuration that should be loaded early ininit.el
because other configuration code depend on them.workaround
: Section contains a workaround for Emacs or package bug, which can be removed if and when the issues are fixed upstream.
File header comment indicating the filename, along with declaring any file-specific variables.
One file-specific variable that should generally be set is enabling lexical-binding
(link), which has the following benefits:
- Closures.
- Better performance.
- Less bugs.
(concat ";;; " feature ".el --- " summary " -*- lexical-binding: t; -*-")
Tangled initialization files are timestamped to track when they were last generated.
(concat ";; Generated: " (current-time-string))
Author information and where to get the newest version of this configuration.
;; Author: matheuristic
;; URL: https://github.com/matheuristic/emacs-config
File descriptions.
;; Emacs initialization configuration file, symlink or copy to
;; ~/.emacs.d/init.el or $XDG_CONFIG_HOME/.emacs.d/init.el
;; In Emacs 26, the sequence of initialization is
;; 1. package.el
;; 2. init.el
Emacs has a text GUI interface for customizing the editor, and
settings configured with this interface are saved in custom-file
.
To avoid the M-x customize
settings clobbering the tangled
initialization files (which it does by default), set custom-file
to
to something that is not the Emacs init file.
Other local configuration should go into a lisp/init-local.el
file
in user-emacs-directory
(usually ~/.emacs.d/lisp/init-local.el
).
This file should provide the init-local
feature when it is loaded.
In the below configuration, this is loaded after initialization but
before custom-file
is loaded.
Template for init-local.el
file.
;;; init-local.el --- Emacs local config file -*- lexical-binding: t; -*- ;;; Commentary: ;; Emacs configuration that is machine-local, typically loaded before ;; the Customize file. ;; This file should be located at lisp/init-local.el within ;; `user-emacs-directory', typically ~/.emacs.d/lisp/init-local.el ;;; Code: ;; Local configuration code goes here ... (provide 'init-local) ;;; init-local.el ends here
;; store Customize settings in a separate file, custom.el
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
;; load local init configuration and Customize settings on startup
(add-hook 'after-init-hook
(lambda ()
(require 'init-local nil t) ; don't raise errors
(load custom-file 'noerror))
10) ; load this after regular `after-init-hook' functions
Various utility functions used in multiple config locations, usually non-interactive and added to hooks or as advice to other functions, and the custom variables that get used by them and/or other specific-use functions.
my-system-open-command
should be set to the system command for
opening generic file paths and URLS, for example xdg-open
in Linux
and open
in macOS.
(defcustom my-system-open-command "xdg-open"
"System command to open file/URL according to preferred app by filetype.
Usually \"xdg-open\" on Linux and \"open\" on Mac."
:type 'string
:group 'convenience)
Define a function that performs a laundry list of useful context-specific actions useful after jumping to a new location.
Some examples of these context actions:
- Run
org-show-context
after jumping to an Org buffer location.
(defun my-after-jump-context-actions (&rest _)
"Useful context actions to perform after jumping to a new location.
This is meant for use with `advice-add' with the :after
combinator.
One useful context action example is to run `org-show-context'
after jumping to an Org buffer location to ensure the region
around the new point location is visible."
(cond ((eq major-mode 'org-mode) (org-show-context))))
Save current buffer and bury it.
(defun my-save-and-bury-buffer (&rest _)
"Save and bury the current buffer."
(save-buffer)
(bury-buffer))
Generally, use-package
does not install the ELPA-compatible
repository version of a package when there is already a built-in
version of it. This function works around that limitation by forcing
installed of the ELPA-compatible repository version if it is not
already present in the ELPA package cache.
;; hacky workaround to install ELPA/MELPA version of a package
;; adapated from https://github.com/jwiegley/use-package/issues/319
(defun my-install-elpa-package (pkg-symb)
"Install the ELPA-compatible repository version of package PKG-SYMB.
Useful for working around `use-package' behavior of not
installing the repository version of a package when a built-in
version is present (even if pinned to a specific repository)."
(let ((pkg-pattern (concat package-user-dir
"/" (symbol-name pkg-symb) "-[0-9]*")))
(unless (file-expand-wildcards pkg-pattern)
(package-install (elt (cdr (assoc pkg-symb
package-archive-contents))
0)))))
When multiple versions of an Elisp file exist (compiled and uncompiled), load the newest.
;; when multiple versions of a package are installed, load the newest
(setq load-prefer-newer t)
Add the lisp/
and site-lisp/
directories in the user Emacs
directory to the load path to facilitate loading of user maintained
and local copies of third-party packages.
;; add user packages in lisp/ to load path
(defvar lisp-dir (expand-file-name "lisp" user-emacs-directory))
(unless (file-exists-p lisp-dir) (make-directory lisp-dir))
(add-to-list 'load-path lisp-dir)
(dolist (project (directory-files lisp-dir t "\\w+"))
(when (file-directory-p project) (add-to-list 'load-path project)))
;; add third-party packages in site-lisp/ and its subdirs to load path
(defvar site-lisp-dir (expand-file-name "site-lisp" user-emacs-directory))
(unless (file-exists-p site-lisp-dir) (make-directory site-lisp-dir))
(add-to-list 'load-path site-lisp-dir)
(dolist (project (directory-files site-lisp-dir t "\\w+"))
(when (file-directory-p project) (add-to-list 'load-path project)))
Set ELPA-compatible repositories to fetch and install packages from, and their priorities. When the packages with the same name exist on multiple repositories, the version on the repository with the highest priority is preferred.
The following package repositories are the most well-known:
- GNU Emacs Lisp Package Archive (ELPA). This is the default package repository for Emacs.
- Milkypostman’s Emacs Lisp Package Archive (MELPA). This is an unofficial package repository containing a large selection of packages. Packages in this repository are vetted at time of initial inclusion, which are automatically rebuilt on source updates. Does not contain some packages from EmacsWiki due to security risks (some are in MELPA because they were mirrored on Github)
- Milkypostman’s Emacs Lisp Package Archive Stable (MELPA Stable). This is a version of MELPA that only builds tagged releases. Has less packages compared to MELPA.
- Org Emacs Lisp Package Archive (Org).
This is the official Org package repository that contains the newest
version of
org
(also in ELPA), along withorg-plus-contrib
that contains all contributed files (not in ELPA).
Only ELPA and MELPA are used here so the latest package versions are installed, and because there isn’t generally a need for all the contributed files for Org.
;; set ELPA-compatible package repositories and their priorities
(setq package-archives '(("ELPA" . "https://elpa.gnu.org/packages/")
("MELPA" . "https://melpa.org/packages/"))
package-archive-priorities '(("ELPA" . 1)
("MELPA" . 2)))
Initialize package loading support. Disable auto-package loading and load packages explicitly for faster initialization times.
;; initialize package.el
(require 'package)
(package-initialize)
Download the use-package if not already on the system. Load it, which will provide configuration macros for installing, loading and configuring packages. Also load its subpackage bind-key, which provides macros for key bindings.
;; bootstrap use-package, provides configuration macros
;; for info, see https://github.com/jwiegley/use-package
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
;; preload use-package and bind-key packages
;; configure imenu support for the `require' and `use-package' keywords
(eval-when-compile
(setq use-package-enable-imenu-support t)
(require 'use-package)
(require 'bind-key)
(setq use-package-always-ensure t)) ;; default to ":ensure t"
;; gather use-package stats, "M-x use-package-report" to see report
(setq use-package-compute-statistics t)
At times, it is useful to be able to reinstall and reload an Emacs package without restarting.
Define a convenience function my-reinstall-package
that unloads all
loaded features associated with a given package, reinstalls the
package and reloads the features that were unloaded.
Adapted from:
;; convenience function to reinstall and reload an Emacs package
(require 'cl-macs)
(require 'seq)
(defun my-package-reinstall (pkg)
"Prompts for an installed package PKG and reinstalls it.
All loaded features that correspond to Elisp filenames in the
package install directory (but not its subdirectories) are
unloaded, the package reinstalled, and the previously unloaded
features are reloaded."
(interactive (list (intern (completing-read
"Reinstall package: "
(mapcar #'car package-alist)))))
(let* ((pkg-desc (car (alist-get pkg package-alist)))
(pkg-dir (file-name-as-directory
(cl-struct-slot-value 'package-desc 'dir pkg-desc)))
(pkg-files (directory-files pkg-dir nil "\\.el$"))
(pkg-features (mapcar
(lambda (fname)
(intern (file-name-sans-extension fname)))
pkg-files))
(reload-features (seq-filter 'featurep pkg-features)))
(dolist (feat reload-features)
(ignore-errors ; handle when pkg is a dependency of another package
(unload-feature feat t)))
(package-reinstall pkg)
(dolist (feat reload-features)
(require feat))))
Backend package that allows editing regions in a separate buffer, much like
how C-c '
works in Org source blocks.
This is used by other packages, like markdown-mode
.
;; edit regions in separate buffers, used by other packages like markdown-mode
(use-package edit-indirect)
Company is a text completion framework for Emacs that supports pluggable back-ends and front-ends for retrieving and displaying completion candidates. Many other Emacs packages support this.
This can get in the way for non-programming modes, so it is enabled by default only in programming modes.
;; text completion framework
(use-package company
:defer t
:init
(with-eval-after-load 'prog-mode
(add-hook 'prog-mode-hook 'company-mode))
(setq company-dabbrev-downcase nil
company-idle-delay 0.5
company-minimum-prefix-length 2
company-selection-wrap-around t
company-show-numbers t ;; use M-<num> to directly choose completion
company-tooltip-align-annotations t))
Backup files to the ~/.backup/
directory, keeping only the newest three versions.
;; backup files to ~/.backup/
(let ((backup-dir (expand-file-name "~/.backup/")))
(when (not (file-directory-p backup-dir))
(make-directory backup-dir t))
(setq backup-directory-alist `(("." . ,backup-dir))
version-control t ;; use version numbers for backups
kept-new-versions 3 ;; number of newest versions to keep
kept-old-versions 0 ;; number of oldest versions to keep
delete-old-versions t ;; don't ask before deleting old backups
backup-by-copying t)) ;; backup by copying instead of renaming
The built in recentf provides functionality to track and list recently opened files.
;; recently opened files
(setq recentf-max-menu-items 10
recentf-max-saved-items 100
recentf-auto-cleanup 'mode) ;; clean up recent list when turning on mode
(recentf-mode 1)
;; exclude compressed files
(add-to-list 'recentf-exclude ".gz")
(add-to-list 'recentf-exclude ".xz")
(add-to-list 'recentf-exclude ".zip")
;; exclude source code files in installed packages from ELPA-compatible repos
(add-to-list 'recentf-exclude
(concat "^" (expand-file-name "elpa/" user-emacs-directory)))
;; exclude files opened with SSH so TRAMP is not spammed with stat calls
;; exclude files opened as the superuser with su or sudo
(add-to-list 'recentf-exclude "^/\\(?:scp\\|ssh\\|su\\|sudo\\)?:")
;; exclude files from /var/folder as these are temp files
(add-to-list 'recentf-exclude "^/var/folders")
;; exclude files in `org-agenda-files'
;; these files are quickly accessible from their respective tooling
(add-hook 'after-init-hook
(lambda ()
(dolist (file-list (list org-agenda-files))
(dolist (exclude-file file-list)
(add-to-list 'recentf-exclude
(concat "^" exclude-file))))))
;; binding for recentf
(global-set-key (kbd "C-c f") #'recentf-open-files)
;; select file to open from `recentf-list' using `completing-read'
(defun my-recentf-find-file ()
"Use `completing-read' to find a recent file."
(interactive)
(find-file (completing-read "Find recent file: " recentf-list)))
;; binding for `my-recentf-open-files' when in recentf dialog buffers
(define-key recentf-dialog-mode-map (kbd "f") #'my-recentf-find-file)
Enable saveplace to automatically save location in file, so that the next time the file is visited the point will automatically go to the last place it was at during the previous visit.
(save-place-mode 1)
Enable savehist to automatically save minibuffer command history,
which can be leverage by different completion packages.
Other history (like search history, registers, the kill ring, and the
macro ring) can also be saved.
The default history file location is history
in
the user-emacs-directory
directory, and can be changed by setting
the savehist-file
variable. The number of items saved is determined
by the history-length
variable.
;; save minibuffer and other history across sessions
;; don't persist kill-ring if in the habit of copy-pasting passwords
(setq history-delete-duplicates t
history-length 100
;; if `desktop-save-mode' is enabled, it saves `register-alist'
;; and `search-ring' by default so it is unnecessary to add
;; those to `savehist-additional-variables'
savehist-additional-variables '(Info-history-list
;; kill-ring
kmacro-ring
regexp-search-ring
;; register-alist
last-kbd-macro
;; search-ring
shell-command-history))
;; enable save history mode
(savehist-mode 1)
Protect the *scratch*
and *Message*
buffers, locking them to make
them unkillable.
;; protect these buffers, locking them to make them unkillable
(dolist (buf '("*scratch*" "*Messages*"))
(with-current-buffer buf
(emacs-lock-mode 'kill)))
Use Ibuffer to manage buffers.
;; advanced buffer management with Ibuffer
(setq ibuffer-expert t ; skip extraneous confirm messages
ibuffer-show-empty-filter-groups nil)
(global-set-key (kbd "C-x C-b") #'ibuffer)
Set default rules for grouping files in Ibuffer. Collapse some groups, like Org journal files, by default.
;; configure Ibuffer filter groups
(with-eval-after-load 'ibuffer
(defun my-ibuffer-org-agenda-files-filter ()
"Ibuffer filter for checking if current buffer is an Org agenda file.
Specifically, the current buffer is checked to see if it is in
`org-agenda-files', is the agenda inbox file
`my-org-agenda-inbox', or is the someday inbox file
`my-org-someday-inbox'."
(let* ((bufname (buffer-file-name))
(fname (and bufname (file-truename bufname))) ; filename if a file buffer, nil otherwise
(agenda-fnames (mapcar #'file-truename (append (org-agenda-files) ; agenda and inbox filenames
(list my-org-agenda-inbox
my-org-someday-inbox)))))
(and fname
(member fname agenda-fnames))))
(setq ibuffer-saved-filter-groups
;; files are grouped by the first matching filter group in the list
'(("default"
("Emacs" (or (name . "^\\*scratch\\*$")
(name . "^\\*Messages\\*$")))
("Calendar" (or (name . "^\\*?[Cc]alendar.*$")
(name . "^diary$")))
("DocView" (mode . doc-view-mode))
("Images" (mode . image-mode))
("Web" (or (mode . eww-mode)
(mode . eww-bookmark-mode)))
("Shell" (or (mode . eshell-mode)
(mode . shell-mode)
(mode . term-mode)))
("Data" (or (name . ".csv")
(name . ".json")
(mode . nxml-mode)))
("Programming" (derived-mode . prog-mode))
("Agenda" (or (mode . org-agenda-mode)
(predicate . (my-ibuffer-org-agenda-files-filter))))
("Org" (derived-mode . org-mode))
("Text" (derived-mode . text-mode))
("Fundamental" (mode . fundamental-mode))
("Dired" (mode . dired-mode))
("Magit" (derived-mode . magit-mode))
("Help" (or (derived-mode . apropos-mode)
(derived-mode . help-mode)
(derived-mode . Info-mode))))))
(defun my-ibuffer-filter-groups-setup ()
"Custom configuration to load when a new Ibuffer buffer gets created."
;; use "default" saved filter groups list by default
(ibuffer-switch-to-saved-filter-groups "default"))
(add-hook 'ibuffer-mode-hook #'my-ibuffer-filter-groups-setup))
buffer-expose visualizes buffers in a grid of windows and allows the
user to switch to a selected buffer in that grid. Integrates with
ace-window if available (set buffer-expose-auto-init-aw
to t
for
automatically initializing grid buffer views with ace-window enabled).
;; visual buffer switching using a grid of windows
(use-package buffer-expose
:init
(setq buffer-expose-show-current-buffer t)
;; set auto initialization with ace-window if it is loaded
(with-eval-after-load 'ace-window
(setq buffer-expose-auto-init-aw t)))
This minor mode manages the addition and removal of the file being
visited by the current buffer to and from revert-without-query
.
Enabling the mode adds the file to revert-without-query
, and
disabling the mode does the opposite. This is useful when working with
viewing a file which is changing frequently on disk.
(define-minor-mode revert-without-query-mode
"Minor mode for adding/removing current file to/from `revert-without-query'.
Enabling the minor mode adds the file to `revert-without-query'.
Disabling the minor mode removes the file from `revert-without-query'.
This minor mode has no effect when the buffer is not visiting a file."
:init-value nil
:lighter " 🅠"
:keymap nil
;; match filename from `find-file-noselect'
(let ((fname (abbreviate-file-name (expand-file-name
(buffer-file-name)))))
(if buffer-file-name
(if (symbol-value revert-without-query-mode)
(progn
(setq revert-without-query (add-to-list 'revert-without-query fname))
(message "Buffer revert without query ON."))
(setq revert-without-query (remove fname revert-without-query))
(message "Buffer revert without query OFF."))
(message "Current buffer is NOT visiting a file."))))
Winner mode allows the traversal of window configuration history using
C-c <left>
(undo) and C-c <right>
(redo).
;; traverse window config changes, C-c left/right to undo/redo
;; uncomment to not bind C-c left/right keys by default
;; (setq winner-dont-bind-my-keys t)
;; enable winner-mode at end of initialization
(add-hook 'after-init-hook #'winner-mode)
Use M-o
(easier than C-x o
) to cycle between visible windows in a
frame.
;; more convenient bindings for `other-window' and `other-frame'
(global-set-key (kbd "M-o") #'other-window)
Helper function to rotate the buffers in the current frame’s windows. This rotation preserves the window configuration but shifts the buffers displayed in each window.
(defun my-rotate-window-buffers (rotations)
"Rotate buffers in the windows of the current frame ROTATIONS times.
ROTATIONS can be negative, which rotates in the opposite direction."
(interactive "P")
(let* (;; windows that do not contain transient buffers
(windows (seq-filter (lambda (w)
(not
(string= (buffer-name
(window-buffer w))
transient--buffer-name)))
(window-list)))
(num-windows (length windows)))
(if (not (> num-windows 1))
(message "Only one window in the frame. Nothing to rotate.")
(let* (;; original window order properties
(window-props (mapcar (lambda (w)
`(:buffer ,(window-buffer w)
:start ,(window-start w)
:point ,(window-point w)))
windows))
;; new window order after rotation
(window-moves (mapcar
(lambda (k)
(elt windows (mod (+ k rotations)
num-windows)))
(number-sequence 0 (1- num-windows))))
;; create alist for easier looping later
(wins-props (cl-mapcar #'cons window-moves window-props)))
;; iteratively assign orig window props in new window order
(dolist (w-p wins-props)
(let ((win (car w-p))
(prop (cdr w-p)))
(set-window-buffer-start-and-point
win
(plist-get prop :buffer)
(plist-get prop :start)
(plist-get prop :point))))))))
(defun my-rotate-buffers-forward ()
"Rotate buffers in current frame's windows forward."
(interactive)
(my-rotate-window-buffers 1))
(defun my-rotate-buffers-backward ()
"Rotate buffers in current frame's windows backward."
(interactive)
(my-rotate-window-buffers -1))
;; bind "C-x 4 [" and "C-x 4 ]" to rotation of window buffers
(global-set-key (kbd "C-x 4 [") #'my-rotate-buffers-backward)
(global-set-key (kbd "C-x 4 ]") #'my-rotate-buffers-forward)
Automatically focus on newly opened help buffers in existing or new windows.
;; automatically focus on help windows when they are opened
(setq help-window-select t)
Resize frame by pixels rather than by characters (the default). This helps to resolve issues with fullscreen and maximized windows not filling up the entire screen in some window managers when the screen width and height are not multiples of the character width and height.
;; resize frames by pixel instead of by character
(setq frame-resize-pixelwise t)
transpose-frame allows for the rotation of the frame to get a new window layout that is rotated from the original.
See the package Elisp code for more details.
(use-package transpose-frame
:bind (("C-x 5 [" . rotate-frame-anticlockwise)
("C-x 5 ]" . rotate-frame-clockwise)))
Use M-O
(easier than C-x 5 o
) to cycle between frames.
;; more convenient bindings for `other-frame'
(global-set-key (kbd "M-O") #'other-frame)
desktop.el provides capabilities for saving and restoring sessions manually and automatically.
Configuration:
- Enable
desktop-save-mode
which automatically saves on exit and loads on entry, but setdesktop-auto-save-timeout
to disable default behavior of auto-saving on a timer.
;; settings for desktop.el
;; desktops are saved to ~/.emacs.d/.emacs.desktop
;; and locks are saved to ~/.emacs.d/.emacs.desktop.lock
;; - enable desktop-save-mode to save on exit and load on entry;
;; this is added to `after-init-hook' to avoid a prompt on startup
;; warning about the desktop file being in use that occurs when
;; `desktop-save-mode' is enabled before initialization is done,
;; even though the Emacs process PID is the owner of the lock file;
;; might be specific to emacs-mac port
;; - set `desktop-autosave-timeout' to nil to disable timer auto-saves
;; - restore frames to their original displays
;; - re-use existing frames
(setq desktop-auto-save-timeout nil
desktop-restore-in-current-display nil
desktop-restore-reuses-frames t
desktop-files-not-to-save (concat "\\("
(mapconcat
'identity
'("\\`/[^/:]*:"
"(ftp)\\'"
"\\.log"
"\\.gz")
"\\|"
)
"\\)"))
(add-hook 'after-init-hook
(lambda ()
(desktop-save-mode 1)
(desktop-read))
50) ; load after all other `after-init-hook' functions
Eshell is an Elisp shell-like command interpreter that can be used in place of term-mode
and bash
.
More information on Eshell usage.
Customizations:
- Increase the size of the history input ring from
128
to1024
. - Don’t review quick commands (those that have no output and returns a
0
exit code indicating success). - Have space go to the end of the buffer when it is visible.
- Have point jump to the beginning of the last command after each command.
- Load em-smart which adds some quality of life improvements.
Usage note:
- When searching history using the beginning of a command,
eshell-previous-matching-input-from-input
(UP
),M-p
orC-c M-r
is much friendlier thaneshell-previous-matching-input
(M-r
). Type the first few characters of the command, and press theUP
orM-p
key repeatedly to cycle only through the matching commands in the history. Copied from StackOverflow answer here.
(setq eshell-history-size 1024
eshell-review-quick-commands nil
eshell-smart-space-goes-to-end t
eshell-where-to-jump 'begin)
(require 'em-smart)
Some “visual” commands present and update a full-screen interface instead of streaming output to stdout. Run these commands inside a separate term buffer instead.
;; enable Eshell to spawn visual commands inside
(require 'em-term)
;; run visual commands and subcommands in term sessions
(dolist (cmd '("htop" "lftp" "ssh" "vi" "vim" "watch"))
(add-to-list 'eshell-visual-commands cmd))
(dolist (subcmd '(("tail" "-f" "-F")
("sudo" "vi" "vim")
("vagrant" "ssh")))
(add-to-list 'eshell-visual-subcommands subcmd))
;; ensure Git does not launch a pager for easier usage with eshell
(setenv "GIT_PAGER" "")
Provide a binding to a wrapper function that spawns or switches to a named Eshell buffer. This allows for easier access to and management of multiple Eshell buffers.
;; adapted from https://arte.ebrahimi.org/blog/named-eshell-buffers
(defun my-eshell-with-name ()
"Prompts for the name of a eshell buffer to open or switch to.
If the NAME given at the prompt is not an existing eshell buffer,
a new one named *eshell*<NAME> will be opened. If no name is
provided, the default interactive `eshell' command is run."
(interactive)
(let* ((my-es-bufs (seq-filter
(lambda (buf)
(string-match-p "*eshell*" (buffer-name buf)))
(buffer-list)))
(my-es-buf-name-list (mapcar #'buffer-name my-es-bufs))
(my-es-buf-name (completing-read
"Eshell Buffer : " my-es-buf-name-list)))
(if (member my-es-buf-name (mapcar #'buffer-name (buffer-list)))
(switch-to-buffer my-es-buf-name)
(if (string= "" my-es-buf-name)
(eshell)
(progn
(eshell 42)
(rename-buffer (concat "*eshell*<" my-es-buf-name ">")))))))
Extend bookmark.el to support bookmarking Eshell buffers opened to a
specific directory. Usual bookmarking commands apply, like C-x r m
to capture a bookmark and C-x r l
to restore a bookmark.
(use-package eshell-bookmark
:after eshell
:config
(add-hook 'eshell-mode-hook #'eshell-bookmark-setup))
Make the command interpreter (comint) prompts read-only.
;; make shell prompts read-only
(setq comint-prompt-read-only t)
Kill term buffers after session end on a “q” keypress.
;; kill term buffers with 'q' after session end
(defun term-handle-exit--close-buffer-on-cmd (&rest args)
"Kill term buffer with 'q' after session exit."
(when (null (get-buffer-process (current-buffer)))
(use-local-map (let ((keymap (make-sparse-keymap)))
(define-key keymap (kbd "q")
(lambda ()
(interactive)
(kill-buffer (current-buffer))))
keymap))))
(advice-add 'term-handle-exit :after #'term-handle-exit--close-buffer-on-cmd)
Define some convenience functions for interaction with the currently active tmux session.
tmux-send
prompts for a command to send and sends it.tmux-resend
resends the previously sent command from the current buffer.
;; convenience functions for sent commands to an active tmux session
;; adapted from https://explog.in/notes/tmux.html
;; track previously sent tmux commands on per-buffer basis
(setq tmux-send--last-command nil)
(make-variable-buffer-local 'tmux-send--last-command)
(defun tmux-send (command)
"Sends the specified COMMAND to the currently active tmux pane."
(interactive "sCommand: ")
(setq tmux-send--last-command command)
(call-process "tmux" nil nil nil "send-keys" command "Enter"))
(defun tmux-resend ()
"Resends previously sent command to currently active tmux pane."
(interactive)
(if tmux-send--last-command
(call-process "tmux" nil nil nil "send-keys" tmux-send--last-command "Enter")
(message "No previously sent command from the current buffer!")))
Ediff is a built-in tool that visualizes the standard Unix diff and patch programs.
Configuration:
- Always set control window in the same frame as the diff’ed files.
;; always set up Ediff control window in the same frame as the diff,
;; open with horizontal window split instead of the default vertical
(setq ediff-split-window-function 'split-window-horizontally
ediff-window-setup-function 'ediff-setup-windows-plain)
Add an Ediff command for copying diff regions for a hunk from both buffers A and B to C when in a 3-way diff job, for example when resolving Git merge conflicts.
Adapted from here.
;; copy diff hunk from buffers A and B to C in 3-way Ediff
;; adapted from https://stackoverflow.com/a/29757750
(defun ediff-copy-A-and-B-to-C (arg)
"Copies ARGth diff region from both buffers A and B to C.
ARG is a prefix argument. If nil, copy the current difference region."
(interactive "P")
(ediff-barf-if-not-control-buffer)
(if (eq arg '-) (setq arg -1)) ;; translate neg arg to -1
(if (numberp arg) (ediff-jump-to-difference arg))
(ediff-copy-diff ediff-current-difference nil 'C nil
(concat
(ediff-get-region-contents ediff-current-difference
'A
ediff-control-buffer)
(ediff-get-region-contents ediff-current-difference
'B
ediff-control-buffer)))
;; recenter with rehighlighting, but no messages
(ediff-recenter))
(add-hook 'ediff-keymap-setup-hook
(lambda ()
(when ediff-3way-job
(define-key ediff-mode-map "d" 'ediff-copy-A-and-B-to-C))))
(with-eval-after-load 'ediff-help
(setq ediff-long-help-message-compare3
(concat ediff-long-help-message-compare3
" |"
" d -copy A + B regions to C
"
)))
Dired is a built-in directory editor for Emacs.
Additionally load some built-in extra Dired features, including a
global binding C-x C-j
to directly jump to a Dired buffer for the
directory containing the current buffer.
(require 'dired-x) ; extra features
(require 'dired-aux) ; even more extra features
(setq dired-auto-revert-buffer 'dired-directory-changed-p ; when revisiting Dired buffers, refresh if dir has changed on disk
dired-dwim-target t ; use neighboring dired buffer as default target dir
dired-listing-switches "-alhvFG" ; more readable file listings
dired-omit-files (concat dired-omit-files "\\|^\\..+$") ; omit dot files in dired-omit-mode
dired-recursive-copies 'always ; always copy recursively
dired-recursive-deletes 'always) ; always delete recursively
;; uncomment below to automatically update Dired buffers every
;; `auto-revert-interval' seconds, at cost of some slowdown
;; (add-hook 'dired-mode-hook #'auto-revert-mode) ; auto-refresh on file change
(add-hook 'dired-mode-hook #'dired-hide-details-mode) ; hide details initially
Add binding for opening a file at point in Dired using the system file
open dispatcher (typically xdg-open
on Linux and open
on Mac).
;; bind "z" in dired-mode to open file at point using system command
;; to open files by type
(with-eval-after-load 'dired
(defun dired--open-file-at-pt ()
"Opens file at point in Dired using system open command.
This opens the file using the preferred application by filetype."
(interactive)
(let ((filename (dired-get-file-for-visit)))
(start-process "default-app"
nil
my-system-open-command
filename)))
(define-key dired-mode-map (kbd "z") #'dired--open-file-at-pt))
Use spaces (soft tabs) to indent by default instead of actual tab characters (hard tabs).
Use C-q TAB
to input hard tabs if necessary.
;; indent with soft tabs; use C-q <TAB> for real tabs
(setq-default indent-tabs-mode nil)
Add a convenience function for yanking (pasting) from the kill-ring
with completion.
Completion support is provided through completing-read
, which is
shadowed by completion frameworks like Icomplete, Ido, Ivy, etc.
Configuration:
- Rebind yank to completion-enabled yank function.
(defun my-yank-from-kill-ring ()
"Yank from the kill ring into buffer at point or region.
Uses `completing-read' for selection, which is set by Ido, Ivy, etc."
(interactive)
(let ((to-insert (completing-read
"Yank : " (cl-delete-duplicates kill-ring :test #'equal))))
;; delete selected buffer region if any
(if (and to-insert (region-active-p))
(delete-region (region-beginning) (region-end)))
;; insert the selected entry from the kill ring
(insert to-insert)))
;; bind `my-yank-from-kill-ring'
(global-set-key (kbd "C-c y") #'my-yank-from-kill-ring)
Use the built-in delsel package to support deleting the selected region on delete or some character input, which is the behavior in line with typical user interface conventions.
;; typing text replaces the active (i.e. selected) region, if any is selected
(delete-selection-mode)
Single spacing after sentences.
For abbreviations, use non-breaking spaces that can be input with
\\{}nbsp
in Org documents, or with C-x 8 SPC
for the UTF-8
non-breaking space character.
;; use single spaces after sentences
(setq sentence-end-double-space nil)
EasyPG Assistant is a GnuPG interface for Emacs.
;; enable transparent editing of GPG files
(require 'epa-file)
(epa-file-enable)
Iedit mode enables editing multiple occurances of the same word in the buffer simultaneously.
Usage notes:
C-;
to edit occurrences of the word under point within the buffer, orC-u 0 C-;
to edit occurrences only within the current function. The rest of the list describes bindings wheniedit-mode
is active.M-H
restricts iedit to the current function.- Selecting a region while in
iedit-mode
and callingC-'
(or callingM-x iedit-mode
) again restricts iedit to that region. M-I
restricts iedit to current lineM-{
andM-}
expands iedit region one-line at a time upwards and downwards (add a prefix argument to reverse instead). This is useful after restricting iedit to the current line, current function or a selected region.M-p
andM-n
expands up and down to the next occurrence.M-C
toggles case sensitivity when searching for occurrences.C-'
while editing to toggle narrowing to occurrence lines.TAB
andS-TAB
to cycle between occurrences.C-;
when editing is done to apply changes.
(use-package iedit
:init (setq iedit-toggle-key-default (kbd "C-;"))
:config
;; advise iedit functions that jump to new point locations to
;; perform context actions after they are run
(dolist (jump-fun '(iedit-next-occurrence
iedit-prev-occurrence
iedit-goto-first-occurrence
iedit-goto-last-occurrence
iedit-expand-to-occurrence))
(advice-add jump-fun :after #'my-after-jump-context-actions)))
multiple-cursors.el is package that enables the creation of multiple cursors in Emacs that all do the same thing simultaneously.
;; multiple cursors
;; using `set-rectangular-region-anchor' is probably the easiest
;; see https://emacs.stackexchange.com/a/773
(use-package multiple-cursors
:bind (("M-C" . mc/edit-lines)
("M-V" . set-rectangular-region-anchor)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-S-<mouse-1>" . mc/toggle-cursor-on-click))
:init (setq mc/always-run-for-all nil
mc/always-repeat-command nil
mc/insert-numbers-default 1)
:config
;; decrease width of the multiple-cursors bar
;; setting a height of 1 ends up rendering a thick bar
;; probably because it is too small a value
(set-face-attribute 'mc/cursor-bar-face nil :height 10))
Paredit provides a minor mode for structured editing S-expression data.
Enable it for editing Emacs Lisp buffers and the minibuffer.
Also configure it so its commands integrate appropriately with
delete-selection-mode
.
;; structured editing of S-expressions with Paredit
(use-package paredit
:commands paredit-mode
:bind (:map paredit-mode-map
("{" . paredit-open-curly)
("}" . paredit-close-curly))
:hook ((emacs-lisp-mode . paredit-mode)
;; when in minibuffer via `eval-expression`
(eval-expression-minibuffer-setup . paredit-mode)
;; *scratch* default mode
(lisp-interaction-mode . paredit-mode))
:config
;; non-terminal bindings, see https://www.racket-mode.com/#paredit
(unless terminal-frame
(define-key paredit-mode-map (kbd "M-[") #'paredit-wrap-square)
(define-key paredit-mode-map (kbd "M-{") #'paredit-wrap-curly))
;; make delete-selection-mode work within paredit-mode
(with-eval-after-load 'delsel
(put 'paredit-forward-delete 'delete-selection 'supersede)
(put 'paredit-backward-delete 'delete-selection 'supersede)
(put 'paredit-open-round 'delete-selection t)
(put 'paredit-open-square 'delete-selection t)
(put 'paredit-doublequote 'delete-selection t)
(put 'paredit-newline 'delete-selection t)))
The undo-tree package allows the traversal of the undo history as a tree, which makes utilizing Emacs rather flexible undo/redo capabilities much easier.
Default bindings are C-/
to undo, C-S-/
to redo, and C-x u
to
open a new window whose buffer where the undo history is presented as
a tree and can be navigated using the regular movement keys.
;; traverse undo history as a tree, default binding is "C-x u"
(use-package undo-tree
:init (setq undo-tree-visualizer-relative-timestamps nil)
:config
;; enable globally
(global-undo-tree-mode))
M-z
is bound by default to zap-to-char
that deletes from the point
to (including) the next occurrence of a given character, which is like
d f <char>
in Vim.
There is also a more useful variant zap-up-to-char
which deletes up
to but not including the next occurrence of a given character, which
is like d t <char>
in Vim.
Configuration:
- Rebind
M-z
tozap-up-to-char
. UseC-u ARG M-z
to delete up to theARG
-th next occurrence of a character.
;; bind over `zap-to-char' (defaults to "M-x") with `zap-up-to-char'
(global-set-key [remap zap-to-char] #'zap-up-to-char)
Bind cycle-spacing
in place of just-one-space
as it is more
versatile, utilizing a single command to cycle between one space
around point, no spaces around point and original spacing by calling
it consecutively.
(global-set-key [remap just-one-space] #'cycle-spacing)
Join current and next line, like J
in Vim.
;; Join next line to end of current line, like "J" in Vim
(defun my-join-next-line ()
"Join the next line to the end of the current line."
(interactive)
(let ((col (current-column)))
(join-line -1)
(move-to-column col)))
(global-set-key (kbd "C-S-j") #'my-join-next-line)
Create a new line below/above the current one and move point
there, like o~/~O
in Vim.
(defun my-open-line-below (n)
"Open a new line below and go to it.
With arg N, insert N newlines."
(interactive "*p")
(end-of-line)
(newline n)
(indent-according-to-mode))
(defun my-open-line-above (n)
"Open a new line above and go to it.
With arg N, insert N newlines."
(interactive "*p")
(beginning-of-line)
(newline n)
(forward-line (- n))
(indent-according-to-mode))
;; bind over `open-line' ("C-o") with `my-open-line-below'
(global-set-key [remap open-line] #'my-open-line-below)
;; binding for `my-open-line-above
(global-set-key (kbd "C-S-o") #'my-open-line-above)
;; use built-in DWIM versions of default editing commands
;; note that comment insertion command ("M-;") is already DWIM-ified
(global-set-key (kbd "M-u") #'upcase-dwim)
(global-set-key (kbd "M-l") #'downcase-dwim)
Use server-mode
(toggle) or server-start
to start a server from
the current Emacs session.
Clients for the server can be created using emacsclient
command.
Note: Quitting the main Emacs session that initiated the server mode also quits the server and closes the attached clients. If the goal is to have a headless Emacs server always running, start it with one of the following.
# run headless as a daemon in the background $ /Applications/Emacs.app/Contents/MacOS/bin/emacsclient --daemon # run headless as a daemon in the foreground $ /Applications/Emacs.app/Contents/MacOS/bin/emacsclient --fg-daemon
One option is also to have a headless Emacs server spawn on login, see link.
Send a SIGUSR1
signal to the Emacs process to start or restart the
server process.
See link for more info.
;; server mode restart safety valve
(defun restart-emacs-server ()
"Restarts an Emacs server."
(interactive)
(server-force-delete)
(server-mode 1)
(message "Restarted Emacs server."))
;; bind SIGUSR1 signal to call `server-restart'
(define-key special-event-map [sigusr1] #'restart-emacs-server)
To test the signal handler, have Emacs send a signal to itself:
(signal-process (emacs-pid) 'sigusr1)
To call the signal handler from the command line, run:
$ pkill -SIGUSR1 -i emacs
markdown-mode provides a major mode for editing Markdown files.
Pandoc (GitHub) is used for exporting to other formats, so it should be installed (e.g. using conda or by using a release binary).
Configuration:
- Enable
markdown-mode
automatically for the common suffixes, except forREADME.md
wheregfm-mode
is enabled instead for editing Github-flavored Markdown. - Pandoc is configured to generate standalone (not self-contained) HTML files and to render TeX expressions using KaTeX (GitHub).
;; major mode for editing Markdown files
(use-package markdown-mode
:commands (markdown-mode gfm-mode)
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init
;; place header markup only at the start of a line
;; syntax highlighting in fenced code blocks
;; use underscores for italics instead of asterisks
(setq markdown-asymmetric-header t
markdown-fontify-code-blocks-natively t
markdown-italic-underscore t)
;; if available, use pandoc for converting markdown files
(when (executable-find "pandoc")
(setq markdown-command (concat "pandoc --from markdown --to html"
" --standalone"
" --katex"
" --highlight-style=pygments"
" --quiet") ; suppress warnings
markdown-command-needs-filename t)))
(use-package markdown-toc
:after markdown-mode)
yaml-mode provides a major mode for editing YAML files.
;; provides a major mode for editing YAML files
(use-package yaml-mode
:commands yaml-mode
:mode ("\\.ya?ml\\'" . yaml-mode))
Org-mode is a major mode for document editing, formatting and
organizing, designed to help with taking notes, planning and authoring
in Emacs.
Org files typically have filenames with the .org
suffix.
Note: The version of Org packaged with a given Emacs version is
typically adequate, but it is often better to install the newest
version of org
from ELPA or the Org repository through
list-packages
.
Examples of good configurations include this.
Use the ELPA version of Org that more closely tracks master.
;; install ELPA version of Org
(my-install-elpa-package 'org)
org-force-cycle-archived
in Org versions <9.4 is bound to C-TAB
which shadows the default tab-bar-mode
bindings for tab-next
.
For these older versions, unbind C-TAB
in org-mode-map
and bind
org-force-cycle-archived
to C-c C-TAB
which matches the bindings
in newer Org versions.
;; rebind `org-force-cycle-archived' in older Org versions to not
;; conflict with the `tab-next' default binding
(with-eval-after-load 'org
(when (version< (org-version) "9.4")
(define-key org-mode-map (kbd "<C-tab>") nil)
(org-defkey org-mode-map (kbd "C-c C-<tab>") #'org-force-cycle-archived)))
Customizations:
- Use
~/org/
as the main Org directory. - Set
inbox.org
inorg-directory
as the default filing location to be utilized later. - Enforce task dependencies
;; set Org directory and inbox file
(setq org-directory (file-truename (file-name-as-directory (expand-file-name "~/org"))))
(defvar my-org-agenda-inbox (concat org-directory "agenda/inbox.org")
"Path to Org agenda inbox.")
(defvar my-org-someday-inbox (concat org-directory "agenda/someday.org")
"Path to Org someday inbox.")
(defvar my-org-journal-file (concat org-directory "agenda/journal.org")
"Path to Org journal file.")
(defvar my-org-scratch-file (concat org-directory "agenda/scratch.org")
"Path to Org scratch file.")
(defvar my-org-websnippet-file (concat org-directory "agenda/websnippets.org")
"Path to Org websnippet file.")
;; basic Org-mode settings
(setq org-adapt-indentation nil ; don't auto-indent when promoting/demoting
org-attach-dir-relative t ; use relative directories when setting DIR property using `org-attach-set-directory'
;; org-blank-before-new-entry '((heading . nil) ; don't auto-add new lines
;; (plain-list-item . nil)) ; same as above
org-catch-invisible-edits 'show-and-error
org-confirm-babel-evaluate nil ; don't confirm before evaluating code blocks in Org documents
org-cycle-separator-lines 2 ; collapse single item separator lines when cycling
org-deadline-warning-days 3 ; warn starting 3 days before deadline
org-edit-src-content-indentation 2
org-enforce-todo-checkbox-dependencies t
org-enforce-todo-dependencies t
org-fontify-done-headline t
org-fontify-quote-and-verse-blocks t
org-fontify-whole-heading-line t
org-goto-interface 'outline-path-completion
org-hide-emphasis-markers nil
org-hide-leading-stars nil
org-highlight-latex-and-related '(latex script entities) ; highlight LaTeX fragments with the `org-highlight-latex-and-related' face
org-image-actual-width (list (/ (display-pixel-width) 3)) ; auto-resize displayed images to one-third of display width
org-link-file-path-type 'adaptive ; use relative paths for links to files in Org file dir or subdirs, absolute otherwise
org-log-done 'time ; log time that task was marked DONE
org-log-into-drawer t
org-outline-path-complete-in-steps nil
org-pretty-entities t
org-pretty-entities-include-sub-superscripts nil ; don't render sub/superscripts in-buffer
org-return-follows-link t
org-src-fontify-natively nil ; don't syntax color org source blocks
org-src-preserve-indentation t ; preserve src code block indentation on export and when switching btw org buffer and edit buffer
org-src-strip-leading-and-trailing-blank-lines t
org-src-tab-acts-natively t
org-src-window-setup 'current-window ; reuse Org file window for editing source blocks when using "C-c '"
org-startup-folded t
org-startup-indented nil
org-treat-S-cursor-todo-selection-as-state-change nil
org-use-fast-todo-selection t
org-use-speed-commands t)
;; make sure UUIDs generated for Org usage are alway upcased, which
;; solves issues with synced directories, for example Linux generates
;; lower case UUIDs while Mac generates upper case UUIDs.
(with-eval-after-load 'org-id
(defun org-id-uuid--around-upcase (orig-fun &rest args)
"Advice for `org-id-uuid' to upcase the uuids it outputs.
ORIG-FUN is the original function.
ARGS are the arguments provided to ORIG-FUN."
(let ((uuid (apply orig-fun args)))
(upcase uuid)))
(advice-add 'org-id-uuid :around
'org-id-uuid--around-upcase))
Bind over org-open-line
with a variant that calls
my-open-line-below
instead.
(defun my-org-open-line-below (n)
"Insert a new row in tables, call `my-open-line-below' elsewhere.
If `org-special-ctrl-o' is nil, call `my-open-line-below' everywhere.
As a special case, when a document starts with a table, allow to
call `open-line' on the very first character."
(interactive "*p")
(if (and org-special-ctrl-o (/= (point) 1) (org-at-table-p))
(org-table-insert-row)
(my-open-line-below n)))
;; bind over `org-open-line' to call `my-org-open-line-below' instead
;; making it consistent with customized global-mode-map "C-o"
(with-eval-after-load 'org-keys
(define-key org-mode-map (kbd "C-o") #'my-org-open-line-below))
Possible Org task states:
- TODO: Inactive task.
- NEXT: Active task. Keep to just a few (less open loops).
- DONE: Completed task.
- HOLD: Paused inactive task.
- WAIT: Paused active task, waiting for external action.
- CANX: Canceled task.
;; Set possible Org task states
;; Diagram of possible task state transitions
;; ---------------------
;; | |
;; | V
;; --> TODO.. -> NEXT... -> DONE ----->
;; | Λ | | | Λ Λ |
;; V | | | V | | |
;; HOLD | | WAIT --- |
;; | | | | |
;; V V V V |
;; CANX........... -------------
;; (note records why it was cancelled)
(setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
(sequence "HOLD(h@/!)" "WAIT(w@/!)" "|" "CANX(c@/!)")))
;; Org capture templates
(defun my-org-goto-end-of-org-file ()
"Goto end of selected user file starting from `org-directory'."
(let ((path (read-file-name
"File: " org-directory nil nil nil
(lambda (x) (string-suffix-p ".org" x)))))
(find-file path)
(goto-char (point-max))))
(setq org-capture-templates '(("t" "New Task" entry (file my-org-agenda-inbox)
"* TODO %i%?\n%U")
("l" "Linked Task" entry (file my-org-agenda-inbox)
"* TODO %a%?\n%U")
("s" "Someday Task" entry (file my-org-someday-inbox)
"* TODO %i%?\n%U")
("i" "Interrupt Task" entry (function my-org-goto-end-of-org-file)
"* NEXT %i%?\n%U"
:jump-to-captured t :clock-in t :clock-resume t)
("j" "Journal Entry" entry
(file+olp+datetree my-org-journal-file)
"**** %?\n%T"
:tree-type week :clock-in t :clock-resume t)
("J" "Schedule Journal Entry" entry
(file+olp+datetree my-org-journal-file)
"**** %?\n%T"
:tree-type week :time-prompt t)))
Have Org capture buffers always be maximized. Restore the window configuration after capturing the task.
(with-eval-after-load 'org
;; maximize org-capture buffer
(defun my-org-capture-setup (&rest args)
"Save window configuration prior to `org-capture'."
(set-frame-parameter
nil
'my-org-capture-prior-config
(current-window-configuration)))
(defun my-org-capture-teardown ()
"Restore window configuration prior to `org-capture'."
(let ((prior-window-configuration (frame-parameter
nil
'my-org-capture-prior-config)))
(when prior-window-configuration
(set-window-configuration prior-window-configuration))))
(advice-add 'org-capture :before 'my-org-capture-setup)
(add-hook 'org-capture-mode-hook 'delete-other-windows)
(add-hook 'org-capture-after-finalize-hook 'my-org-capture-teardown))
;; tags (note that tags within the same group are mutually exclusive)
(setq org-tag-alist '((:startgroup) ;; export
("export" . ?9)
("noexport" . ?0)
(:endgroup)
;; prioritization (e.g. Eisenhower matrix)
("important" . ?1)
("urgent" . ?2)
(:newline)
;; entry context, in addition to category
("@work" . ?w)
("@life" . ?l)
("@learn" . ?e)
(:startgroup) ;; special meeting types
("hiring" . ?h)
("managing" . ?m)
("vendor" . ?v)
("sales" . ?s)
("strategy" . ?t)
(:endgroup)))
Useful Org export macros that are enabled globally.
To color text (works with LaTeX and HTML exports):
{{{color(colorname, text)}}}
To insert filler text:
{{{loremipsum}}}
To export different text for LaTeX and for other formats:
{{{if-latex-else(latex-specific text,other text)}}}
;; `org-export' macros
(with-eval-after-load 'ox
;; color macro, {{{color(colorname, text)}}} to use
(push `("color"
.
,(concat "@@latex:\\textcolor{$1}{$2}@@"
"@@html:<span style=\"color:$1\">$2</span>@@"))
org-export-global-macros)
;; placeholder text, {{{loremipsum}}} to use
(push `("loremipsum"
.
,(mapconcat 'identity
'("Lorem ipsum dolor sit amet, consectetur"
"adipisicing elit, sed do eiusmod tempor"
"incididunt ut labore et dolore magna"
"aliqua. Ut enim ad minim veniam, quis"
"nostrud exercitation ullamco laboris nisi"
"ut aliquip ex ea commodo consequat. Duis"
"aute irure dolor in reprehenderit in"
"voluptate velit esse cillum dolore eu"
"fugiat nulla pariatur. Excepteur sint"
"occaecat cupidatat non proident, sunt in"
"culpa qui officia deserunt mollit anim id"
"est laborum."
"\n\n"
"Curabitur pretium tincidunt lacus. Nulla"
"gravida orci a odio. Nullam varius, turpis"
"et commodo pharetra, est eros bibendum elit,"
"nec luctus magna felis sollicitudin mauris."
"Integer in mauris eu nibh euismod gravida."
"Duis ac tellus et risus vulputate vehicula."
"Donec lobortis risus a elit. Etiam tempor."
"Ut ullamcorper, ligula eu tempor congue,"
"eros est euismod turpis, id tincidunt sapien"
"risus a quam. Maecenas fermentum consequat"
"mi. Donec fermentum. Pellentesque malesuada"
"nulla a mi. Duis sapien sem, aliquet nec,"
"commodo eget, consequat quis, neque. Aliquam"
"faucibus, elit ut dictum aliquet, felis nisl"
"adipiscing sapien, sed malesuada diam lacus"
"eget erat. Cras mollis scelerisque nunc."
"Nullam arcu. Aliquam consequat. Curabitur"
"augue lorem, dapibus quis, laoreet et,"
"pretium ac, nisi. Aenean magna nisl, mollis"
"quis, molestie eu, feugiat in, orci. In hac"
"habitasse platea dictumst.")
" "))
org-export-global-macros)
;; flow control for latex-specific text and otherwise
;; {{{if-latex-else(latex text, other text)}}} to use
(push '("if-latex-else"
.
"(eval (if (org-export-derived-backend-p
org-export-current-backend
'latex)
$1
$2))")
org-export-global-macros))
By default, Org mode uses the system defaults for several file types. Have Org mode open PDF files within Emacs instead of using the default (which is using the system viewer).
;; have Org mode open PDF files within Emacs
(with-eval-after-load 'org
(push '("\\.pdf\\'" . emacs) org-file-apps))
Org-mode provides agenda views that give an overview of open action
items or events with specific scheduled or deadline dates in Org files
specified by org-agenda-files
.
A number of customizations are done here, including defining
a view that displays 3-day agenda and undated TODO
entries.
Important: Don’t add scheduled dates or deadlines unless necessary, it clutters up the agenda view.
Note: Archiving DONE
and CANX
items every so often helps to keep
Org agenda parsing and operations speedy (link):
M-x org-agenda
to bring up the agenda menu.t
to listTODO
items.N r
(whereN
is the number corresponding to a task state) to select theDONE
(N=3
in this configuration) or theCANX
(N=6
in this configuration) task state.*
to select all listed items.B $
to bulk archive the selected items to their respective<filename>.org_archive
files.
Workflow:
- Add new items to inbox.
- Periodically refile inbox items into appropriate agenda project file.
- Process the refiled items as per normal TODO task workflow.
Additional notes:
inbox.org
andsomeday.org
are not part of the Agenda files, but are accessed via specific agenda views.
;; org-agenda settings:
;; - narrow to subtree in org-agenda-follow-mode ("F" in agenda)
;; - full-frame Agenda view
;; - use ~/ORG-DIRECTORY/*.org files as Org agenda files
;; - de-duplicate entries with both a scheduled and deadline date
;; - don't show entries with scheduled or deadline dates that are done
(setq org-agenda-follow-indirect t
org-agenda-restore-windows-after-quit t
org-agenda-skip-deadline-if-done t
org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled
org-agenda-skip-scheduled-delay-if-deadline nil
org-agenda-skip-scheduled-if-deadline-is-shown t
org-agenda-skip-scheduled-if-done t
org-agenda-start-on-weekday nil
org-agenda-window-setup 'only-window
org-agenda-files (seq-filter
(lambda (x)
(and
(not (string-suffix-p my-org-agenda-inbox x))
(not (string-suffix-p my-org-someday-inbox x))
(not (string-suffix-p my-org-scratch-file x))
(not (string-suffix-p my-org-websnippet-file x))))
(file-expand-wildcards (concat org-directory "agenda/*.org"))))
;; add separator between each day in agenda view
(setq org-agenda-format-date
(lambda (date)
(let* ((datestr (org-agenda-format-date-aligned date))
(separator-width (- (window-width)
(string-width datestr)
1)))
(concat "\n" datestr " " (make-string separator-width ?_)))))
;; helper functions for custom agenda commands
(defun my-org-agenda-to-deadline-prefix-str ()
"Descriptor string for days to deadline for Org entry at point."
(let ((deadline (org-get-deadline-time (point))))
(when deadline
(let ((days-left (org-time-stamp-to-now (format-time-string "%F" deadline))))
(cond ((< days-left (- 1)) (format "%3d d. ago" (- days-left)))
((= days-left (- 1)) (format " Yesterday" days-left))
((= days-left 0) (format " Today" days-left))
((= days-left 1) (format " Tomorrow" days-left))
((> days-left 1) (format " In %3d d." days-left)))))))
;; custom agenda commands
(setq org-agenda-custom-commands
`(("." "Today's agenda and all TODO entries"
((agenda "" ((org-agenda-span 1)))
(todo "NEXT" ((org-agenda-todo-ignore-with-date nil)
(org-agenda-sorting-strategy '(ts-up priority-down effort-up category-keep alpha-up))))
(todo "WAIT" ((org-agenda-todo-ignore-with-date nil)
(org-agenda-sorting-strategy '(ts-up priority-down effort-up category-keep alpha-up))))
(todo "TODO" ((org-agenda-todo-ignore-with-date nil)
(org-agenda-sorting-strategy '(ts-up priority-down effort-up category-keep alpha-up))))
(todo "HOLD" ((org-agenda-todo-ignore-with-date nil)
(org-agenda-sorting-strategy '(ts-up priority-down effort-up category-keep alpha-up))))))
("u" "Undated TODO entries"
((todo "NEXT" ((org-agenda-todo-ignore-with-date t)
(org-agenda-sorting-strategy '(priority-down effort-up category-keep alpha-up))))
(todo "WAIT" ((org-agenda-todo-ignore-with-date t)
(org-agenda-sorting-strategy '(priority-down effort-up category-keep alpha-up))))
(todo "TODO" ((org-agenda-todo-ignore-with-date t)
(org-agenda-sorting-strategy '(priority-down effort-up category-keep alpha-up))))
(todo "HOLD" ((org-agenda-todo-ignore-with-date t)
(org-agenda-sorting-strategy '(priority-down effort-up category-keep alpha-up))))))
("d" "Deadlines"
((tags-todo "DEADLINE<\"<today>\"" ; tasks that are past deadline
((org-agenda-prefix-format " %(my-org-agenda-to-deadline-prefix-str) %i %-12:c%?-12t% s")
(org-agenda-sorting-strategy '(deadline-up priority-down scheduled-up effort-up category-keep alpha-up))))
(tags-todo "DEADLINE>=\"<today>\"" ; tasks with upcoming deadlines
((org-agenda-prefix-format " %(my-org-agenda-to-deadline-prefix-str) %i %-12:c%?-12t% s")
(org-agenda-sorting-strategy '(deadline-up priority-down scheduled-up effort-up category-keep alpha-up))))))
("i" "Inbox entries"
((alltodo "" ((org-agenda-files '(,my-org-agenda-inbox))
(org-agenda-sorting-strategy '(priority-down deadline-up scheduled-up effort-up category-keep alpha-up))))))
("o" "Someday entries"
((alltodo "" ((org-agenda-files '(,my-org-someday-inbox))
(org-agenda-sorting-strategy '(priority-down deadline-up scheduled-up effort-up category-keep alpha-up))))))))
Set up Org refile targets:
- Current buffer up to a max depth of 9 levels.
- Org Agenda files corresponding to non-directory entries of
org-agenda-files
at the top-level of each file.
;; allow refiling up to 9 levels deep in the current buffer
;; and 3 levels deep in Org agenda files
;; allow refiling to the top level
(setq org-refile-targets `((nil . (:maxlevel . 9)) ;; current buffer
;; top-level of regular `org-agenda-files' files
(,(seq-filter
'file-regular-p
org-agenda-files) . (:level . 0)))
org-refile-use-outline-path 'file
org-refile-allow-creating-parent-nodes 'confirm)
(add-hook 'org-mode-hook #'visual-line-mode)
Org-mode supports an Org → LaTeX → PDF build chain for compiling Org documents to PDF files.
By default, this build process utilizes the base latex
compiler and
does not handle BibTeX bibliography database files (.bib
files).
It is better change to this to a more modern compiler like LuaTeX for better font and unicode support, and add a BibTeX compiler like Biber to the build chain.
;; compile Org documents to PDF with LuaTeX and Biber
(when (executable-find "lualatex")
(with-eval-after-load 'org
(setq org-latex-pdf-process
'("lualatex -interaction nonstopmode -output-directory %o %f"
"lualatex -interaction nonstopmode -output-directory %o %f"))
(if (executable-find "biber")
(push "biber %b" org-latex-pdf-process))
(push "lualatex -interaction nonstopmode -output-directory %o %f"
org-latex-pdf-process)))
Load the built-in Org backend for exporting Org documents to Markdown.
;; load Org backend for exporting to Markdown
(with-eval-after-load 'org
(require 'ox-md))
Copy and evaluate (using C-c C-c
) the following block to an Org mode
buffer, modify the :match
option parameters to include/exclude tags
of interest and add/remove/change the other options as appropriate.
See the Org docs for using the clock table and the tag match syntax.
#+end
Enable Org pre-9.2 structure expansions, e.g. <s
followed by TAB.
See Org documentation for more info.
;; Enable Org pre-9.2 structure expansions, e.g. ~<s~ followed by TAB
(with-eval-after-load 'org
(require 'org-tempo nil :noerror))
Flymake is a built-in on-the-fly syntax checker, with updated versions available from the GNU ELPA repository.
To display the error message at point in the minibuffer, do C-h .
while the point is over an error.
To define new Flymake backend, refer to the docstring of
flymake-diagnostic-functions
, the Flymake manual or the code of
existing backends.
If using this, it is typical to not enable Flycheck.
;; basic Flymake customizations
(setq flymake-no-changes-timeout 0.5 ;; auto check buffer change wait time
flymake-start-on-save-buffer nil) ;; don't run checks when saving
;; deferred Flymake customizations
(with-eval-after-load 'flymake
;; don't use legacy Flymake checker
(remove-hook 'flymake-diagnostic-functions #'flymake-proc-legacy-flymake)
;; function for toggling Flymake diagnostics window
(defun my-toggle-flymake-diagnostics ()
"Toggles flymake diagnostics window for current buffer."
(interactive)
(if flymake-mode
(let* ((buf-name (buffer-name (current-buffer)))
(flymake-winds (condition-case nil
(get-buffer-window-list
(concat "*Flymake diagnostics for " buf-name "*"))
(error nil))))
(if flymake-winds
(dolist (wind flymake-winds) (quit-window nil wind))
(flymake-show-diagnostics-buffer)))))
;; shorten Flymake mode line symbol
(defun my-flymake-modeline-filter (ret)
"Filter function for `flymake--mode-line-format`."
(setf (seq-elt (car ret) 1) " FlyM")
ret)
(advice-add #'flymake--mode-line-format :filter-return
#'my-flymake-modeline-filter)
;; convenience bindings
(define-key flymake-mode-map (kbd "C-c ! n") #'flymake-goto-next-error)
(define-key flymake-mode-map (kbd "C-c ! p") #'flymake-goto-prev-error)
(define-key flymake-mode-map (kbd "C-c ! l") #'my-toggle-flymake-diagnostics))
;; enable Flymake when editing Emacs Lisp buffers
(add-hook 'emacs-lisp-mode-hook #'flymake-mode)
Flymake sometimes has issues with remote files edited over TRAMP. For some problematic file, the easiest solution is to add an Emacs file variable to the first line of the file to turn off Flymake (see following code for a Python file example).
# -*- eval:(flymake-mode-off) -*-
;; TODO add :around advice to flymake-mode to not enable mode
;; when editing a remote file
el-patch provides a way to customize Emacs Lisp functions.
(use-package el-patch
:demand t)
Modify lisp-indent-function
so that it indents property lists in
Emacs Lisp (and other Lisps) in the expected manner (like in Clojure).
Uses functions from the el-patch
package, so make sure that package
is loaded before this code block.
;; Modifies lisp indentation to handle property lists used as
;; data structures
;; --- DEFAULT BEHAVIOR
;; `(:token ,token
;; :token-quality ,quality)
;; ---
;; --- DESIRED BEHAVIOR
;; `(:token ,token
;; :token-quality ,quality)
;; ---
;; Copied from https://emacs.stackexchange.com/questions/10230/
(with-eval-after-load 'lisp-mode
(el-patch-defun lisp-indent-function (indent-point state)
"This function is the normal value of the variable `lisp-indent-function'.
The function `calculate-lisp-indent' calls this to determine
if the arguments of a Lisp function call should be indented specially.
INDENT-POINT is the position at which the line being indented begins.
Point is located at the point to indent under (for default indentation);
STATE is the `parse-partial-sexp' state for that position.
If the current line is in a call to a Lisp function that has a non-nil
property `lisp-indent-function' (or the deprecated `lisp-indent-hook'),
it specifies how to indent. The property value can be:
* `defun', meaning indent `defun'-style
(this is also the case if there is no property and the function
has a name that begins with \"def\", and three or more arguments);
* an integer N, meaning indent the first N arguments specially
(like ordinary function arguments), and then indent any further
arguments like a body;
* a function to call that returns the indentation (or nil).
`lisp-indent-function' calls this function with the same two arguments
that it itself received.
This function returns either the indentation to use, or nil if the
Lisp function does not specify a special indentation."
(el-patch-let (($cond (and (elt state 2)
(el-patch-wrap 1 1
(or (not (looking-at "\\sw\\|\\s_"))
(looking-at ":")))))
($then (progn
(if (not (> (save-excursion (forward-line 1) (point))
calculate-lisp-indent-last-sexp))
(progn (goto-char calculate-lisp-indent-last-sexp)
(beginning-of-line)
(parse-partial-sexp (point)
calculate-lisp-indent-last-sexp 0 t)))
;; Indent under the list or under the first sexp on the same
;; line as calculate-lisp-indent-last-sexp. Note that first
;; thing on that line has to be complete sexp since we are
;; inside the innermost containing sexp.
(backward-prefix-chars)
(current-column)))
($else (let ((function (buffer-substring (point)
(progn (forward-sexp 1) (point))))
method)
(setq method (or (function-get (intern-soft function)
'lisp-indent-function)
(get (intern-soft function) 'lisp-indent-hook)))
(cond ((or (eq method 'defun)
(and (null method)
(> (length function) 3)
(string-match "\\`def" function)))
(lisp-indent-defform state indent-point))
((integerp method)
(lisp-indent-specform method state
indent-point normal-indent))
(method
(funcall method indent-point state))))))
(let ((normal-indent (current-column))
(el-patch-add
(orig-point (point))))
(goto-char (1+ (elt state 1)))
(parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
(el-patch-swap
(if $cond
;; car of form doesn't seem to be a symbol
$then
$else)
(cond
;; car of form doesn't seem to be a symbol, or is a keyword
($cond $then)
((and (save-excursion
(goto-char indent-point)
(skip-syntax-forward " ")
(not (looking-at ":")))
(save-excursion
(goto-char orig-point)
(looking-at ":")))
(save-excursion
(goto-char (+ 2 (elt state 1)))
(current-column)))
(t $else)))))))
fish-mode is a major-mode for working with fish shell scripts.
(use-package fish-mode
:init (setq fish-enable-auto-indent t
fish-indent-offset 4))
wgrep.el makes grep buffers writable with changes pushed to files.
Enable with C-c C-p
when in a *grep*
buffer.
Automatically installed by rg.el
.
;; "C-c C-p" in grep bufs allow writing with changes pushed to files
(use-package wgrep
:config (setq wgrep-auto-save-buffer nil
wgrep-too-many-file-length 10))
Imenu allows jumping to major definitions in a file by name. Have Imenu automatically rescan whenever the content changes.
(setq imenu-auto-rescan t)
imenu-list is an Imenu extension that opens up a side buffer listing the major definitions and headings in the current buffer. This configuration customizes it such that on jumping to a definition or heading, it pulses the destination line once and automatically closes the imenu-list.
;; show imenu as a list in a side buffer
(use-package imenu-list
:defer t
:after imenu
:bind ("C-c I" . imenu-list-smart-toggle)
:config
(setq imenu-list-focus-after-activation t)
;; pulse target after selecting
(add-hook 'imenu-list-after-jump-hook
(lambda () (pulse-momentary-highlight-one-line (point))))
;; close imenu list after going to entry
(advice-add 'imenu-list-goto-entry :after 'imenu-list-quit-window))
Configure Isearch to automatically fold characters when searching, for
instance when searching for a quote "
the search will also match
round quotes “
and ”
, or the left- and right-pointing double angle
quotation marks «
and »
.
References:
;; fold characters when searching with Isearch
(setq search-default-mode #'char-fold-to-regexp)
Change cursor type to a bar. By default, the cursor blinks 10 times
and stops blinking after when idling. Modify blink-cursor-blinks
to
change this number.
;; set cursor after initialization
(setq-default cursor-type 'bar)
Don’t show the cursor in non-selected windows. This is useful as an additional visual indicator for the selected window.
;; hide cursor in non-selected windows
(setq-default cursor-in-non-selected-windows nil)
Remove toolbar and menu to maximize space usage.
;; remove unused UI elements
(if (fboundp 'scroll-bar-mode)
(scroll-bar-mode -1))
(if (fboundp 'tool-bar-mode)
(tool-bar-mode -1))
;; (if (and (not (display-graphic-p))
;; (fboundp 'menu-bar-mode))
;; (menu-bar-mode -1))
Display line numbers by default when editing code.
;; display line numbers by default when editing code
(add-hook 'prog-mode-hook
(lambda ()
(display-line-numbers-mode 1)))
Show trailing whitespace by default when editing code.
;; show trailing whitespace when editing code
(add-hook 'prog-mode-hook
(lambda ()
(setq show-trailing-whitespace t)))
Show the column number of the point location in the mode line.
;; show point location column number in mode line
(setq column-number-mode t)
Show matching parentheses while editing without delay.
;; show matching parentheses with no delay
(setq show-paren-delay 0)
(show-paren-mode 1)
Adding a default frame internal border creates nicer typography at the cost of displaying a few less characters on screen.
;; add internal frame border
(add-to-list 'default-frame-alist
`(internal-border-width . 12))
(defun my-default-frame-border-teardown ()
"Removes internal-border-width entries from `default-frame-alist'."
(setq default-frame-alist
(assq-delete-all 'internal-border-width default-frame-alist)))
;; add teardown function to be run before closing Emacs, which needs
;; to run early when closing so add it to the end of `after-init-hook'
(add-hook 'after-init-hook
(lambda ()
(add-hook 'kill-emacs-hook #'my-default-frame-border-teardown))
t)
Custom package providing a minor mode for censoring text in a buffer. One scenario where this is useful is to hide sensitive content during a screen share in case the buffer is opened accidentally.
censor-mode
is buffer-local and will always apply to the current
buffer when toggled. global-censor-mode
is global and will apply to
all buffers that either satisfy a predicate function or whose name
matches a regexp in censor-include
.
By default, censor-include
matches all buffers with names that have
the .gpg
suffix (the default for GnuPG-encrypted files).
(require 'censor)
too-long-lines-mode provides a global minor mode that uses overlays to truncate very long lines, which can otherwise slow down Emacs. This package is not in MELPA.
An alternative way to reduce slowdown from long lines is using the built-in so-long package, which instead tries to disable major and minor modes that may contribute to the slowdown.
(require 'too-long-lines-mode)
(too-long-lines-mode 1)
Emacs Web Wowser (eww
) is a built-in Emacs web browser.
It supports text and images, but not webfonts or Javascript.
Customizations:
- Use the lightweight version of DuckDuckGo for web searches by default.
- Don’t render images by default.
;; built-in Emacs text web browser
(use-package eww
:ensure nil ;; built-in
:commands (eww eww-follow-link)
:init (setq eww-search-prefix "https://duckduckgo.com/lite?q=")
:config
;; don't render images in HTML pages by default
(setq-default shr-inhibit-images t)
;; toggle for enabling and disabling images
(defun eww--toggle-images ()
"Toggle displaying of images when rendering HTML."
(interactive)
(setq-local shr-inhibit-images (not shr-inhibit-images))
(eww-reload)
(message "Images are now %s" (if shr-inhibit-images "off" "on")))
(define-key eww-mode-map "I" #'eww--toggle-images))
The Emacs usage of GnuTLS has some timing-related issues, especially
with url-retrieve-synchronously
and on GUI Emacs. This appears to be
a sort of race condition, and a bug reporter thinks that GnuTLS is not
properly waiting for the server to write the initial greeting.
See link for more information and a workaround.
(defun open-gnutls-stream--after-sleep-250ms (&rest args)
"Workaround for race condition bug in `open-gnutls-stream'.
Adds 250ms to the opening of GnuTLS connections.
ARGS is a list of the original arguments passed to
`open-gnutls-stream' and is ignored.
See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=930573#10
for more information."
(sleep-for 0 250))
;; add advice to `open-gnutls-stream' to have it sleep for 250ms
;; as a workaround for GnuTLS race condition bug
(advice-add #'open-gnutls-stream :after #'open-gnutls-stream--after-sleep-250ms)
Increased network security settings.
;; increase network security settings
(setq gnutls-verify-error t)
(setq gnutls-min-prime-bits 1024)
(setq network-security-level 'high)
(setq nsm-save-host-names t)
More private HTTP requests.
;; HTTP requests privacy settings
(setq url-cookie-untrusted-urls '(".*")) ;; no cookies
(setq url-privacy-level 'paranoid) ;; more private HTTP requests
(url-setup-privacy-info) ;; apply `url-privacy-level'
Flyspell is a built-in spell checker that comes in two flavors:
flyspell-mode
checks spelling anywhere in the buffer.flyspell-prog-mode
checks spelling only in comments and strings.
When one of the modes is active, unrecognized words are highlighted. To maintain performance, it only checks words that are newly typed or that the point moves across.
Activating the mode also enables a few bindings that act on unrecognized words:
C-c $
pops up menu of corrections for the word before the point.C-.
corrects the current word. Pressing it repeatedly will propose successive correction candidates.C-;
corrects the previous word. Pressing it repeated will propose successively correction candidates.C-,
jumps to the next unrecognized word.
Note: Flyspell uses as its backend a spell checker like Aspell, so one needs to be installed on system. An appropriate dictionary or dictionaries for the spell checker should also be installed.
Run custom after-jump context actions post-~flyspell-goto-next-error~ calls. In particular, when jumping to a position within a folded region that region is automatically expanded after.
;; advise flyspell jump functions to perform context actions after
;; they are run
(with-eval-after-load 'flyspell
(advice-add 'flyspell-goto-next-error :after #'my-after-jump-context-actions))
Unbind some default Flyspell bindings:
C-;
which conflicts with the iedit bindings
;; unbind some Flyspell default bindings that conflict with other more
;; useful bindings
(with-eval-after-load 'flyspell
(define-key flyspell-mode-map (kbd "C-;") nil)) ; `iedit-mode' binding
Aspell is a spell checker. There are a number of alternatives but they are either deprecated (Ispell), similar (Hunspell), or language-specific (Voikko for Finnish).
This or another supported spell checker has to be installed in order for Flyspell to work. Additionally, a dictionary for the spell checker should also be installed.
If using MacPorts, installation instructions follow for Aspell and its English dictionary.
$ sudo port install aspell $ sudo port install aspell-dict-en
dictionary.el provides a frontend for accessing a local or online dictionary server that implements RFC 2229.
;; provides word lookups from a dictionary server
;; `dictionary-server' can be set to "localhost" to use a local
;; dictionary server like dictd or GNU Dico that implements RFC 2229
(use-package dictionary
:init (setq dictionary-server "dict.org"
dictionary-default-dictionary "*"))
The GNU Collaborative International Dictionary of English (GCIDE) is a free dictionary derived from the Webster’s 1913 dictionary (which provides definitions that are fuller and more descriptive than more modern dictionaries) supplemented with newer definitions from Wordnet and other sources.
It is available as a source on dict.org which can be accessed by
dictionary.el
functions.
Downloads are available for use with a local dictionary server. One local dictionary server that has good GCIDE support is GNU Dico, which has a bundled interface module for the dictionary.
typo.el is a minor mode that makes insertion of typographically useful unicode characters easier.
Enabling the buffer-local typo-mode
does the following:
- Automatically convert
'
to‘
and’
, and"
to“
and”
. - Pressing
'
,"
and-
cycles among their different variants. For example, repeatedly pressing-
cycles between en-dash, em-dash, and other dash-like glyphs. - Three periods in a row get automatically replaced by an ellipsis.
Enabling typo-global-mode
does the following:
C-c 8
brings up a unicode insertion dispatcher that is like the standardC-x 8
unicode insertion dispatcher but focused on a different selection of commonly used typographical characters.
(use-package typo)
The real-auto-save package supports per-file auto-saving at regular
time intervals, i.e. a buffer-local auto-save-visited-mode
.
When the buffer-local minor mode real-auto-save-mode
is active, the
buffer is automatically saved every real-auto-save-interval
seconds.
;; buffer-local `auto-save-visited-mode'
(use-package real-auto-save
:defer t
:config (setq real-auto-save-interval 10)) ;; save interval, in seconds
Example use case:
If some directory (say where files in org-agenda-files
are
stored) is a cloud-enabled shared folder and external changes should be
reflected with files auto-saved on changes so that the buffer, local and
remote files are kept in sync, create a .dir-locals.el
file in the folder
with the following contents.
;; Directory-local settings ((nil . ((eval . (auto-revert-mode 1)) ;; auto-revert files (eval . (real-auto-save-mode 1))))) ;; buffer-local auto-save
Set the initial mode of the scratch buffer to fundamental-mode
,
as it can used for anything and not just Emacs Lisp interaction.
S-expressions in the buffer can still be evaluated as Emacs Lisp by
using C-x C-e
if desired.
;; set *scratch* buffer major-mode to fundamental-mode
(setq initial-major-mode 'fundamental-mode)
The default behavior in Emacs when scrolling past the window edge is to recenter the viewport on the point. Modify this to scroll conservatively, moving the viewport one column or line at a time instead of recentering.
;; scroll a line at a time at window edge
(setq scroll-conservatively 101)
Convenience bindings for scroll-up-line
and scroll-down-line
that
scrolls the viewport a line at a time like C-e
and C-y
in Vim.
;; bindings for scrolling line-at-a-time like "C-e" and "C-y" in Vim
(global-set-key (kbd "C-S-e") #'scroll-up-line)
(global-set-key (kbd "C-S-y") #'scroll-down-line)
Supress audio and visual bells. These are super distracting.
;; turn off audio and visual bells
(setq ring-bell-function 'ignore)
Suppress the minibuffer messages that appear whenever a file is auto-reverted.
;; suppress auto-revert minibuffer messages
(setq auto-revert-verbose nil)
Suppress the startup splash screen. Don’t need it.
;; suppress splash screen that appears on startup by default
(setq inhibit-startup-message t)
Very Large Files provides a minor mode vlf-mode
that allows loading
of large file in chunks, trading increased processing time for reduced
memory usage.
When vlf-mode
is active, it exposes a number of commands that
are prefixed by C-c C-v
:
C-c C-v n
andC-c C-v p
moves forward and back through the file by chunk.C-c C-v SPC
shows the chunk starting from the current point.C-c C-v [
andC-c C-v ]
goes to the front and end of the file.C-c C-v l
andC-c C-v j
jumps to a given line in the file and a given chunk number respectively.C-c C-v s
,C-c C-v r
,C-c C-v o
andC-c C-v %
are the forward search, backward search, occur and replace equivalents that act across all the file chunks. Note theC-c C-v %
auto-saves the file when each chunk is processed.C-c C-v f
toggles continuous chunk around the point.C-c C-v +
andC-c C-v -
control chunk size.
The package also provides two commands vlf-ediff-files
and
vlf-ediff-buffers
that compare files and buffers in chunks.
;; visit large files without loading it entirely
(use-package vlf
:config (require 'vlf-setup))
Many Emacs modes cause slowdown when working with buffers containing extremely long lines.
Emacs 27+ includes a package so-long
that if enabled will
automatically disable major and minor modes that can cause extreme
slowdown when visiting files with excessively long lines.
C-c C-c
will revert so-long-mode
after it is activated back to the
original major mode.
See M-x so-long-commentary
for more information.
;; automatically disable major and minor modes that can slow down
;; Emacs when visiting files with long lines, Emacs 27+ only
(when (require 'so-long nil :noerror)
(global-so-long-mode 1)
;; leave major modes alone, only disable minor modes
;; increase threshold before so-long action is invoked
(setq so-long-action 'so-long-minor-mode
so-long-max-lines 10
so-long-threshold 500))
Disambiguate open file buffers with the same name but from different directories.
(require 'uniquify)
(setq uniquify-buffer-name-style 'post-forward-angle-brackets)
Calc is a built-in calculator that uses Reverse Polish notation (RPN).
Use C-x *
to start a Calc dispatch on the expression under the
point, for example:
C-x * c
to callcalc
and open a Calc instance.C-x * e
to enter Embedded mode when the point is on a formula.C-x * q
to run a quick calculation.
See the help buffer surfaced by C-h f calc-dispatch-help
for more
information.
(setq calc-multiplication-has-precedence nil
calc-ensure-consistent-units t
calc-context-sensitive-enter t
calc-undo-length 100
calc-highlight-selections-with-faces nil)
Some commands are disabled by default and show a warning when they are called for the first time.
Enable some of them:
- Horizontal scrolling,
C-x <
andC-x >
- Narrow to region and page,
C-x n n
andC-x n p
- Downcasing and upcasing a text region,
C-x C-l
andC-x C-u
;; Enable some functions disabled by default.
(put 'scroll-left 'disabled nil)
(put 'scroll-right 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
Buffers can be reverted using s-u
but since the Super key is not on
all keyboards it is a good idea to bind it to a function key, which is
<f5>
here.
(global-set-key (kbd "<f5>") #'revert-buffer)
By default, the paths on the remote machine used by TRAMP for
executables is limited to the default remote system paths. The remote
user’s paths (whatever $PATH
is set to when a login shell is
created, usually this is whatever the default is together with any
modifications in the ~/.profile
file) are not included by default.
The TRAMP remote search paths are specified in in the
tramp-remote-path
variable. Adding the tramp-own-remote-path
symbol to this list makes TRAMP also searches the remote user’s paths.
;; add remote user paths to the TRAMP remote search paths
(with-eval-after-load 'tramp-sh
(add-to-list 'tramp-remote-path 'tramp-own-remote-path))
Setting set-mark-command-repeat-pop
to through allows popping of the
mark multiple times by repeating C-SPC
after having run C-u C-SPC
(i.e. C-u C-SPC ... C-SPC
) instead of having to retype C-u C-SPC
(i.e. C-u C-SPC C-u C-SPC ... C-u C-SPC
).
;; repeating "C-SPC" after popping mark with "C-u C-SPC" pops it again
(setq set-mark-command-repeat-pop t)
Define a command for clearing registers.
(defun my-clear-register (register)
"Clear the contents in register REGISTER."
(interactive (list (register-read-with-preview "Clear register: ")))
(set-register register nil)
(message "Cleared register %c" register))
;; global binding for clearing a register's contents
(global-set-key (kbd "C-x r DEL") #'my-clear-register)
xr provides functionality for converting Emacs regexps to rx notation which is easier understand and debug.
Note that these rx forms can be used in place of regular regexps in
Emacs, for example "/\\*\\(?:[^*]\\|\\*[^/]\\)*\\*+/"
is the same
as (rx "/*" (* (| (not "*") (: "*" (not "/")))) (+ "*") "/")
.
Specifically, this package provides these non-interactive functions:
xr
which converts regexp to rx.xr-pp
which converts regexp to rx and pretty prints it.xr-lint
which finds mistakes in regexp.xr-skip-set
,xr-skip-set-pp
andxr-skip-set-lint
which are skip set versions of the above.xr-pp-rx-to-str
which pretty prints an rx expression to a string.
;; convert regexp to rx notation
(use-package xr)
Alternate binding for opening the menu bar using the keyboard when the
keyboard does not have an F10
key.
;; alternative binding for opening the menu bar
(global-set-key (kbd "C-c e m") #'menu-bar-open)
Temporary bindings that are shown in a popup window.
Transient provides functionality for defining transient keymaps.
Transient bindings and definitions in this configuration conform to the following conventions:
- The config calls
(transient-bind-q-to-quit)
which bindsq
to quit any transient like magit. Doing this will also rotate anyq
,Q
andM-q
bindings defined in any transient toQ
,M-q
and unbound respectively. - Globally-accessible transients are always bound to a key sequence
prefixed by
C-c-
and followed by one or more characters (for exampleC-c B
for the basic buffer management hydra orC-c e p
for the Emacs profiler). These should be chosen to not conflict with common packages with bindings that don’t conform to the convention of reservingC-c <letter>
for user bindings (likexcscope.el
with itsC-c s
bindings). - Transients for toggling settings should be prefixed with
C-c l
wherel
is a mnemonic for layer (for example,C-c l v
for the visual layer orC-c l w
for the writing layer). - Transients for accessing global Emacs functionality should be
prefixed with
C-c e
wheree
is a mnemonic for Emacs (for exampleC-c e f
for Emacs frames andC-c e p
for Emacs profiler). - Major mode-specific transients are always bound to
C-c m
. - Transients are always defined in their own section after all other user configuration, such that all dependencies in transients are loaded before any are defined. This simplifies transient definitions (the alternative is to modify already defined transients), but separates package loading code from where their functions are used in transients. Accordingly, take extra care to check all downstream transient definitions are adjusted when removing packages.
References:
- Anyone have good examples for Transient? : emacs
- magit/transient#51 Add some examples of basic transients for reference
(use-package transient
:init
;; convenience function for specifying transient toggle descriptions
(defun transient--make-description (desc is-enabled)
"Return a string description for transient descriptions.
The returned string has format \"DESC [ ]\" if IS-ENABLED is nil
or \"DESC [X]\" if is-enabled is non-nil.
Examples:
(transient--make-description symbol-overlay-mode \"highlight\")
=> \"highlight [x]\"
Example of use with transient suffix definitions in a
`transient-define-prefix' macro:
...
(\"m\" (lambda () (transient--describe-toggle
\"highlight-at-pt\"
symbol-overlay-mode))
symbol-overlay-mode)
...
=> \"m highlight-at-pt [ ]\"
"
(concat desc " " (if is-enabled "[X]" "[ ]")))
:config
;; bind "q" to quit transient popups by default, transient defns
;; binding "q" will attempt to bind "Q" or "M-q" instead
(transient-bind-q-to-quit))
Global transients have global bindings other than C-c m
(that is
reserved for major mode transients) and are always active.
Transient for easier bookmark manipulation and usage.
;; add transient popup for bookmark commands
(transient-define-prefix transient/bookmarks ()
"Various bookmark commands."
["Bookmarks"
["Navigate"
("j" "Jump" bookmark-jump)
("l" "List" list-bookmarks)
]
["Add/Remove"
("s" "Set" bookmark-set)
("d" "Delete" bookmark-delete)
("i" "Insert" bookmark-insert)
]
["File"
("L" "Load" bookmark-load)
("W" "Write" bookmark-write)
]
]
)
(global-set-key (kbd "C-c e B") #'transient/bookmarks)
Transient for buffer management.
(require 'buffer-expose)
(defun transient/buffer--tramp-cleanup-buffers ()
"Clean up all TRAMP buffers and connections with confirm prompt."
(interactive)
(when (y-or-n-p "Cleanup all TRAMP buffers and connections? ")
(tramp-cleanup-all-buffers)))
(defun transient/buffer--kill-other-buffers ()
"Kill all file buffers except the current one."
(interactive)
(when (y-or-n-p "Kill all file buffers except the current one? ")
(seq-each
#'kill-buffer
(delete (current-buffer)
(seq-filter #'buffer-file-name (buffer-list))))))
(defun transient/buffer--indent-region-or-buffer ()
"Indent a selected region, or the buffer otherwise."
(interactive)
(cond
((use-region-p) (indent-region (region-beginning) (region-end)))
(t (indent-region (point-min) (point-max)))))
(defun transient/buffer--untabify-region-or-buffer ()
"Convert tabs to spaces in a selected region, or the buffer otherwise."
(interactive)
(cond
((use-region-p) (untabify (region-beginning) (region-end)))
(t (untabify (point-min) (point-max)))))
(defun transient/buffer--apply-all-hygiene-ops-region-or-buffer ()
"Apply standard hygiene operations for selected region, or buffer otherwise.
The standard hygiene operations include removing trailing
whitespace, indenting and untabifying."
(interactive)
(progn
(whitespace-cleanup)
(transient/buffer--indent-region-or-buffer)
(transient/buffer--untabify-region-or-buffer)))
(defun transient/buffer--open-containing-dir-externally (&optional path)
"Opens the directory containing PATH or the buffer if unspecified externally."
(interactive)
(let* ((my-path (cl-some 'identity (list path
(buffer-file-name)
default-directory)))
(my-full-path (expand-file-name my-path))
(my-dir-path (file-name-directory my-full-path))
(my-process-args (list "my-open-dir" nil
my-system-open-command my-dir-path)))
(apply 'start-process my-process-args)))
(defun transient/buffer--print-minor-modes ()
"Print enabled minor modes for the current buffer."
(interactive)
(let* ((maybe-active-minor-modes (mapcar #'car minor-mode-alist))
(active-minor-modes (seq-filter (lambda (mode)
(and (boundp mode)
(symbol-value mode)))
maybe-active-minor-modes))
;; sort alphabetically
(active-minor-modes-sorted (sort (mapcar #'symbol-name
active-minor-modes)
'string<))
;; five minor modes to a line
(active-minor-mode-tuples (seq-partition active-minor-modes-sorted 5))
(active-minor-mode-tuples-strs (mapcar
(lambda (tup)
(mapconcat #'identity tup " "))
active-minor-mode-tuples))
(msg-str (mapconcat 'identity active-minor-mode-tuples-strs "\n")))
(message msg-str)))
(defun transient/buffer--clone-indirect-buffer-other-window ()
"Create an indirect buffer of the current one in another window.
This wraps `clone-indirect-buffer-other-window' but provides a
name for the cloned indirect buffer ending with \"-INDIRECT\"."
(interactive)
(let ((bufname (generate-new-buffer-name
(concat (buffer-name)
"-INDIRECT"))))
(clone-indirect-buffer-other-window bufname t)))
;; add transient for buffer management commands
(transient-define-prefix transient/buffer ()
"Buffer management commands."
["Buffer"
["Select"
("b" "Switch" switch-to-buffer)
("n" "Next" next-buffer :transient t)
("p" "Previous" previous-buffer :transient t)
("z" "Open external" transient/buffer--open-containing-dir-externally)
]
["Hygiene"
("cr" "Whitespace report" whitespace-report)
("cw" "Whitespace cleanup" whitespace-cleanup)
("ci" "Indent" transient/buffer--indent-region-or-buffer)
("ct" "Untabify" transient/buffer--untabify-region-or-buffer)
("ca" "All hygiene ops" transient/buffer--apply-all-hygiene-ops-region-or-buffer)
]
["File operations"
("r" "Revert" revert-buffer)
("B" "Bury" bury-buffer)
("U" "Unbury" unbury-buffer)
("s" "Save" save-buffer)
("S" "Save all" save-some-buffers)
("k" "Kill" kill-this-buffer)
("K" "Kill matching" kill-matching-buffers)
("o" "Kill others" transient/buffer--kill-other-buffers)
("T" "TRAMP cleanup" transient/buffer--tramp-cleanup-buffers)
("I" "Make indirect" transient/buffer--clone-indirect-buffer-other-window)
]
["Expose"
("ee" "All" buffer-expose)
("em" "Current mode" buffer-expose-current-mode)
("eM" "Major mode" buffer-expose-major-mode)
("ed" "Dired" buffer-expose-dired-buffers)
("e!" "Non-special" buffer-expose-no-stars)
("e*" "Special" buffer-expose-stars)
]
]
[
["Other"
("M" "Minor modes" transient/buffer--print-minor-modes)
("R" (lambda ()
(transient--make-description
"Autorevert"
(and (boundp 'auto-revert-mode) auto-revert-mode)))
auto-revert-mode :transient t)
("M-r" (lambda ()
(transient--make-description
"Revert without query"
revert-without-query-mode))
revert-without-query-mode :transient t)
]
]
)
(global-set-key (kbd "C-c e b") #'transient/buffer)
Transient for modifying Emacs Lisp debugger settings.
(require 'debug)
(defun transient/debugger--list-variables ()
"Print variables configured to invoke the debugger to the minibuffer."
(interactive)
(prin1 (debug--variable-list)))
;; add transient popup for debugger commands
(transient-define-prefix transient/debugger ()
"Emacs debugger settings."
["Emacs debugger settings"
["Toggle"
("1" (lambda ()
(transient--make-description "Debug on error" debug-on-error))
toggle-debug-on-error :transient t)
("2" (lambda ()
(transient--make-description "Debug on quit" debug-on-quit))
toggle-debug-on-quit :transient t)
]
["Invoke on function entry"
("fl" "List" debugger-list-functions)
("fa" "Add" debug-on-entry)
("fc" "Cancel" cancel-debug-on-entry)
]
["Invoke on variable change"
("vl" "List" transient/debugger--list-variables)
("va" "Add" debug-on-variable-change)
("vc" "Cancel" cancel-debug-on-variable-change)
]
]
)
(global-set-key (kbd "C-c e d") #'transient/debugger)
Transient popup for launching Ediff commands.
;; add transient popup for Ediff commands
(transient-define-prefix transient/ediff ()
"Various Ediff launch commands."
["Ediff"
["2 Way"
("b" "Buffers" ediff-buffers)
("f" "Files" ediff-files)
("d" "Directories" ediff-directories)
("c" "Buffer vs File" ediff-current-file)
("~" "File vs Backup" ediff-backup)
]
["3 Way"
("3b" "Buffers" ediff-buffers3)
("3f" "Files" ediff-files3)
("3d" "Directories" ediff-directories3)
]
["Patches"
("pb" "Buffer" ediff-patch-buffer)
("pf" "File" ediff-patch-file)
]
["Regions"
("rl" "Linewise" ediff-regions-linewise)
("rw" "Wordwise" ediff-regions-wordwise)
]
["Windows"
("wl" "Linewise" ediff-windows-linewise)
("ww" "Wordwise" ediff-windows-wordwise)
]
]
)
(global-set-key (kbd "C-c d e") #'transient/ediff)
Transient for editing commands, mostly useful for using Emacs on the
TTY where some key combinations like C-;
don’t get interpreted by
the terminal.
;; add transient popup for various editing commands
(transient-define-prefix transient/edit ()
"Editing commands."
["Edit"
["Completion"
("/" "Dyn. abbrev" dabbrev-expand :transient t) ; built-in
("TAB" "Company" company-complete) ; autoloaded from company-mode.el
]
["Line"
("O" "New line above" my-open-line-above :transient t)
("o" "New line below" my-open-line-below :transient t)
("J" "Join lines" my-join-next-line :transient t)
]
["Multi-cursor" ; functions autoloaded from multiple-cursors.el
("C" "Edit lines" mc/edit-lines)
("V" "Rect select" set-rectangular-region-anchor)
("<" "Previous" mc/mark-previous-like-this :transient t)
(">" "Next" mc/mark-next-like-this :transient t)
]
["Other"
(";" "Iedit" iedit-mode) ; autoloaded from iedit.el
]
]
)
(global-set-key (kbd "C-c e e") #'transient/edit)
Transient for frame management.
(defun transient/frame--previous-frame ()
"Select previous frame."
(interactive)
(other-frame -1))
;; add transient popup for frame commands
(transient-define-prefix transient/frame ()
"Frame management commands."
["Frame"
["Select"
("n" "Next" other-frame)
("p" "Previous" transient/frame--previous-frame)
("s" "By name" select-frame-by-name)
]
["Layout"
("0" "Delete frame" delete-frame)
("1" "Delete other frames" delete-other-frames)
("2" "Create new frame" make-frame-command)
]
["Resize"
("M" "Toggle maximized" toggle-frame-maximized :transient t)
("f" "Toggle fullscreen" toggle-frame-fullscreen :transient t)
]
]
)
(global-set-key (kbd "C-c e f") #'transient/frame)
Transient for accessing Emacs help.
;; add transient popup for help commands
(transient-define-prefix transient/help ()
"Various help commands."
["Help"
["Apropos"
("aa" "Symbol" apropos)
("ac" "Command" apropos-command)
("ad" "Documentation" apropos-documentation)
("al" "Library" apropos-library)
("av" "Variable" apropos-variable)
("aV" "Value" apropos-value)
]
["Describe"
("db" "Bindings" describe-bindings)
("dc" "Char" describe-char)
("df" "Function" describe-function)
("dF" "Face" describe-face)
("dk" "Key" describe-key)
("dm" "Mode" describe-mode)
("dp" "Package" describe-package)
("ds" "Syntax" describe-syntax)
("dv" "Variable" describe-variable)
]
["Info"
("ia" "Apropos" info-apropos)
("ib" "Browse" info)
("if" "File" info-lookup-file)
("ik" "Keywords" info-finder)
("is" "Symbol" info-lookup-symbol)
]
["Other"
("lf" "List faces" list-faces-display)
("ve" "View messages" view-echo-area-messages)
("vl" "View lossage" view-lossage)
("w" "Where is" where-is)
]
]
)
(global-set-key (kbd "C-c H h") #'transient/help)
Transient for manipulating and using Emacs keyboard macros. For an example, see the writeup on EmacsWiki.
;; add transient for keyboard macros
(transient-define-prefix transient/keyboard-macros ()
"Keyboard macro commands. Tries to adhere to \"C-x C-k\" bindings."
["Keyboard Macros"
["Actions"
("C-s" "Start" kmacro-start-macro) ; also "C-x ("
("C-k" "Call last" kmacro-end-or-call-macro) ; also "C-x )"
("C-r" "Call last on region" apply-macro-to-region-lines)
]
["Ring"
("C-n" "Cycle next" kmacro-cycle-ring-next :transient t)
("C-p" "Cycle prev" kmacro-cycle-ring-previous :transient t)
("C-v" "View last" kmacro-view-macro :transient t)
("C-d" "Delete head" kmacro-delete-ring-head :transient t)
]
["Edit"
("e" "Named" edit-kbd-macro)
("RET" "Last" kmacro-edit-macro)
("l" "Lossage" kmacro-edit-lossage)
("SPC" "Step" kmacro-step-edit-macro)
]
]
[
["Bind/Name"
("b" "Bind to key" kmacro-bind-to-key)
("n" "Name last" kmacro-name-last-macro)
("x" "To register" kmacro-to-register)
]
["Other"
("i" "Insert named" insert-kbd-macro)
]
]
)
(global-set-key (kbd "C-c k") #'transient/keyboard-macros)
Transient for manipulating and managing marks and markers.
(defun transient/marks-and-markers--xref-pop-marker-stack-all ()
"Pop back to where `xref-find-definitions' was first invoked.
\\[xref-find-definitions] is the current binding for `xref-find-definitions'."
(interactive)
(let ((ring xref--marker-ring))
(when (ring-empty-p ring)
(user-error "Marker stack is empty"))
(let ((marker (ring-remove ring nil))) ;; oldest marker
(switch-to-buffer (or (marker-buffer marker)
(user-error "The marked buffer has been deleted")))
(goto-char (marker-position marker))
(set-marker marker nil nil)
(run-hooks 'xref-after-return-hook)
(xref-clear-marker-stack)))) ;; clear the rest of the marker stack
(defun transient/marks-and-markers--push-mark ()
"Push location of point into the mark ring."
(interactive)
(push-mark))
(defun transient/marks-and-markers--pop-mark ()
"Pop the top location the mark ring and jump to it."
(interactive)
(set-mark-command t))
(defun transient/marks-and-markers--push-marker ()
"Push location of point onto the marker stack."
(interactive)
(xref-push-marker-stack))
(defun transient/marks-and-markers--clear-marker-stack ()
"Clear the marker stack."
(interactive)
(xref-clear-marker-stack)
(message "Cleared `xref--marker-ring'"))
(transient-define-prefix transient/marks-and-markers ()
"Commands for manipulating and managing marks and markers."
["Marks/Markers"
["Mark"
("SPC" "Push" transient/marks-and-markers--push-mark)
("RET" "Pop" transient/marks-and-markers--pop-mark :transient t)
("DEL" "Pop global" pop-global-mark :transient t)
]
["Marker"
("." "Push" transient/marks-and-markers--push-marker)
("," "Pop" xref-pop-marker-stack :transient t)
("<" "Pop all" transient/marks-and-markers--xref-pop-marker-stack-all)
("c" "Clear stack" transient/marks-and-markers--clear-marker-stack)
]
]
)
(global-set-key (kbd "C-c M") #'transient/marks-and-markers)
Transient for accessing Org entry points.
;; add transient for accessing Org entry points
(with-eval-after-load 'org
;; file launchers
(defun transient/org-launcher--find-my-org-agenda-inbox ()
"Open a file buffer for `my-org-agenda-inbox'."
(interactive)
(find-file my-org-agenda-inbox))
(defun transient/org-launcher--find-my-org-someday-inbox ()
"Open a file buffer for `my-org-someday-inbox'."
(interactive)
(find-file my-org-someday-inbox))
(defun transient/org-launcher--find-my-org-journal-file ()
"Open a file buffer for `my-org-journal-file'."
(interactive)
(find-file my-org-journal-file))
(defun transient/org-launcher--find-org-websnippet-capture-file ()
"Open a file buffer for `org-websnippet-capture-file'."
(interactive)
(find-file org-websnippet-capture-file))
(defun transient/org-launcher--find-my-org-scratch-file ()
"Open a file buffer for `my-org-scratch-file'."
(interactive)
(find-file my-org-scratch-file))
(transient-define-prefix transient/org-launcher ()
"Launcher for Org entry points."
["Org launcher"
["Main"
("a" "Agenda" org-agenda)
("c" "Capture" org-capture)
("b" "Switchb" org-switchb)
("l" "Store link" org-store-link)
]
["Find-file"
("fi" "Inbox" transient/org-launcher--find-my-org-agenda-inbox)
("fs" "Someday" transient/org-launcher--find-my-org-someday-inbox)
("fj" "Journal" transient/org-launcher--find-my-org-journal-file)
("fw" "Websnippets" transient/org-launcher--find-org-websnippet-capture-file)
("fx" "Scratch" transient/org-launcher--find-my-org-scratch-file)
]
]
)
(global-set-key (kbd "C-c o") #'transient/org-launcher))
Transient for accessing Emacs package management.
;; add transient popup for Emacs package management
(transient-define-prefix transient/package ()
"Emacs package management commands."
["Package management"
("l" "List packages" list-packages)
("i" "Install package" package-install)
("k" "Delete package" package-delete)
("r" "Reinstall package" my-package-reinstall)
("R" "Use-package report" use-package-report) ; requires use-package-compute-statistics set to non-nil before use-package declarations
]
)
(global-set-key (kbd "C-c e P") #'transient/package)
Transient for built in Emacs profiler.
;; add transient for Emacs profiler
(transient-define-prefix transient/profiler ()
"Emacs profiler commands."
[:description (lambda ()
(concat "Profiler | "
(transient--make-description
"CPU"
(profiler-running-p))
" "
(transient--make-description
"Memory"
(profiler-memory-running-p))))
("s" "Start/Reset" profiler-start :transient t)
("p" "Report" profiler-report)
("e" "Stop" profiler-stop :transient t)
]
)
(global-set-key (kbd "C-c e p") #'transient/profiler)
Transient for easier access to register commands.
;; add transient popup for register commands
(transient-define-prefix transient/registers ()
"Register commands."
["Registers"
[("SPC" "Save point" point-to-register)
("w" "Save windows" window-configuration-to-register)
("f" "Save frames" frameset-to-register)
("j" "Jump" jump-to-register)
]
[("s" "Copy region" copy-to-register)
("a" "Append region" append-to-register)
("p" "Prepend region" prepend-to-register)
("r" "Copy rectangle" copy-rectangle-to-register)
]
[("DEL" "Clear" my-clear-register)
("i" "Insert" insert-register)
("l" "List" list-registers)
("v" "View" view-register)
]
]
)
(global-set-key (kbd "C-c R") #'transient/registers)
Transient for accessing Emacs search functionality.
;; add transient popup for search tools
(transient-define-prefix transient/search ()
"Search commands."
["Search"
["Grep"
("gr" "Recursive" rgrep)
("gz" "Recursive (*.gz)" rzgrep)
("gg" "With user args" grep)
("gf" "Via find" grep-find)
]
["Occur in buffers"
("oo" "Current" occur)
("ob" "Matching" multi-occur-in-matching-buffers)
("om" "All" multi-occur)
]
["Query/Replace"
("rs" "String" query-replace)
("rr" "Regexp" query-replace-regexp)
]
["Other"
("." "Find definition" xref-find-definitions)
("w" "EWW web search" eww)
]
]
)
(global-set-key (kbd "C-c S") #'transient/search)
Transient for Emacs server management.
;; add transient popup for Emacs server management
(transient-define-prefix transient/server ()
"Emacs server-related commands."
;; suffix actions don't exit the transient popup by default
:transient-suffix 'transient--do-stay
[:description (lambda ()
(transient--make-description
"Emacs server-mode"
server-mode))
("s" "Toggle server-mode" server-mode)
("r" "Restart server" restart-emacs-server)
("e" "Next server editing buffer" server-edit)
("k" "Stop server and delete connection file" server-force-delete)
]
)
(global-set-key (kbd "C-c e s") #'transient/server)
Transient providing entry points into Eshell and Ansi-Term.
;; add transient popup for shell tools
(transient-define-prefix transient/shell ()
"Various shell tools."
["Shell tools"
["Shell"
("e" "Eshell" my-eshell-with-name)
("a" "ANSI Term" ansi-term)
]
["Tmux"
("ts" "Send" tmux-send)
("tr" "Resend" tmux-resend)
]
]
)
(global-set-key (kbd "C-c T") #'transient/shell)
Transient for accessing system, process and Emacs runtime commands. The Emacs runtime commands also include those for toggling and restarting Emacs as a server.
(defun transient/system--display-current-datetime ()
"Display the current time in the minibuffer."
(interactive)
(message (format-time-string "%Y-%b-%d %l:%M:%S%p %Z %A")))
(defun transient/system--display-emacs-pid ()
"Display the process id of current Emacs process in the minibuffer."
(interactive)
(message (format "%d" (emacs-pid))))
(defun transient/system--display-emacs-build-config ()
"Display the Emacs build configuration in the minibuffer."
(interactive)
(message (mapconcat 'identity
`("Emacs build configuration"
,(format " Build target: %s"
system-configuration)
,(format " Enabled features: %s"
system-configuration-features))
"\n")))
;; add transient popup for system process management and info, and
;; Emacs build and runtime info
(transient-define-prefix transient/system ()
"System process managment, general info and Emacs runtime commands."
["System, process and Emacs runtime"
["Emacs"
("eb" "Build config" transient/system--display-emacs-build-config)
("ei" "Init time" emacs-init-time)
("ep" "Emacs PID" transient/system--display-emacs-pid)
("eu" "Uptime" emacs-uptime)
("ev" "Version" emacs-version)
]
["System"
("sp" "Proced" proced)
("st" "Datetime" transient/system--display-current-datetime :transient t)
("sw" "World time" display-time-world)
]
]
)
(global-set-key (kbd "C-c e S") #'transient/system)
Transient for visual commands and toggles.
;; make sure functions used by visual transient are loaded
(require 'follow)
(require 'hilit-chg)
(require 'hl-line)
(require 'display-line-numbers)
(require 'face-remap)
(require 'whitespace)
(require 'censor)
(require 'too-long-lines-mode)
(defvar-local transient/visual--face-remap-cookies '()
"Alist storing cookies for `face-remap-add-relative' calls.")
(defun transient/visual--toggle-lighten-face (face)
"Toggle brightness of FACE color for emphasis or emphasis."
(let ((face-remap-cookie-old (alist-get
face
transient/visual--face-remap-cookies)))
(if face-remap-cookie-old
(progn
(face-remap-remove-relative face-remap-cookie-old)
(setq transient/visual--face-remap-cookies
(assq-delete-all
face
transient/visual--face-remap-cookies)))
(let* ((light-color (color-lighten-name
(face-attribute face :foreground)
50)) ;; lighten color by 50 percent
(face-remap-cookie-new (face-remap-add-relative
face
:foreground light-color)))
(push `(,face . ,face-remap-cookie-new)
transient/visual--face-remap-cookies)))))
(defun transient/visual--toggle-lighten-font-lock-comment-face ()
"Toggle brightness of `font-lock-comment-face'."
(interactive)
(transient/visual--toggle-lighten-face
'font-lock-comment-face))
(defun transient/visual--toggle-lighten-font-lock-comment-delimiter-face ()
"Toggle brightness of `font-lock-comment-delimiter-face'."
(interactive)
(transient/visual--toggle-lighten-face
'font-lock-comment-delimiter-face))
(defun transient/visual--toggle-lighten-font-lock-doc-face ()
"Toggle brightness of `font-lock-doc-face'."
(interactive)
(transient/visual--toggle-lighten-face
'font-lock-doc-face))
(defun transient/visual--display-toggle-trailing-whitespace ()
"Toggle the display of trailing whitespace."
(interactive)
;; `show-trailing-whitespace' is buffer-local by default
(setq show-trailing-whitespace (not show-trailing-whitespace))
(message "show-trailing-whitespace: %s"
(if show-trailing-whitespace "yes" "no")))
(defun transient/visual--toggle-ligatures ()
"Toggle ligatures.
Currently only works for Emacs Mac port."
(interactive)
(cond ((fboundp 'mac-auto-operator-composition-mode)
(mac-auto-operator-composition-mode))
(t (message "Not implemented for this Emacs build."))))
(defun transient/visual--text-scale-reset ()
"Resets buffer `text-scale-mode-amount' to zero."
(interactive)
(text-scale-set 0))
;; add transient popup for visual commands
(transient-define-prefix transient/visual ()
"Visual commands and toggles."
:transient-suffix 'transient--do-stay
["Visual"
["Display"
("H" (lambda ()
(transient--make-description
"Highlight changes"
highlight-changes-mode))
highlight-changes-mode)
("l" (lambda ()
(transient--make-description
"Line numbers"
display-line-numbers-mode))
display-line-numbers-mode)
("t" (lambda ()
(transient--make-description
"Truncate lines"
truncate-lines))
toggle-truncate-lines)
("w" (lambda ()
(transient--make-description
"Trailing whitespace"
show-trailing-whitespace))
transient/visual--display-toggle-trailing-whitespace)
("W" (lambda ()
(transient--make-description
"Whitespace"
whitespace-mode))
whitespace-mode)
("L" (lambda ()
(transient--make-description
"Hide long lines"
too-long-lines-mode))
too-long-lines-mode)
("$" (lambda ()
(concat "Selective display ["
(if selective-display
(number-to-string selective-display)
" ")
"]"))
set-selective-display)
("P" (lambda ()
(transient--make-description
"Prettify symbols"
prettify-symbols-mode))
prettify-symbols-mode)
("B" (lambda ()
(transient--make-description
"Buffer-specific face"
buffer-face-mode))
buffer-face-mode)
("C-l" "Ligatures" transient/visual--toggle-ligatures)
]
["Cursor"
("b" (lambda ()
(transient--make-description
"Blink"
blink-cursor-mode))
blink-cursor-mode)
("h" (lambda ()
(transient--make-description
"Highlight line"
hl-line-mode))
hl-line-mode)
("p" (lambda ()
(transient--make-description
"Show paren"
show-paren-mode))
show-paren-mode)
("T" (lambda ()
(transient--make-description
"Transient mark"
transient-mark-mode))
transient-mark-mode)
]
["Color"
("cf" (lambda ()
(transient--make-description
"Font locking"
font-lock-mode))
font-lock-mode)
("cc" (lambda ()
(transient--make-description
"Comments"
(null (assq 'font-lock-comment-face
transient/visual--face-remap-cookies))))
transient/visual--toggle-lighten-font-lock-comment-face)
("cC" (lambda ()
(transient--make-description
"Comment delims"
(null (assq 'font-lock-comment-delimiter-face
transient/visual--face-remap-cookies))))
transient/visual--toggle-lighten-font-lock-comment-delimiter-face)
("cd" (lambda ()
(transient--make-description
"Docstrings"
(null (assq 'font-lock-doc-face
transient/visual--face-remap-cookies))))
transient/visual--toggle-lighten-font-lock-doc-face)
]
]
[
[:description (lambda ()
(transient--make-description
"Narrow"
(buffer-narrowed-p)))
("nd" "Defun" narrow-to-defun)
("nr" "Region" narrow-to-region)
("np" "Page" narrow-to-page)
("nw" "Widen" widen)
]
[:description (lambda ()
(concat "Zoom ["
(if text-scale-mode
(number-to-string text-scale-mode-amount)
" ")
"]"))
("+" "Increase" text-scale-increase)
("-" "Decrease" text-scale-decrease)
("0" "Reset" transient/visual--text-scale-reset)
]
["Other"
("m" (lambda ()
(transient--make-description
"Menu bar"
menu-bar-mode))
menu-bar-mode)
("C" (lambda ()
(transient--make-description
"Scroll lock"
(and (boundp 'scroll-lock-mode)
scroll-lock-mode)))
scroll-lock-mode)
("v" (lambda ()
(transient--make-description
"Visual line"
visual-line-mode))
visual-line-mode)
("F" (lambda ()
(transient--make-description
"Follow"
follow-mode))
follow-mode)
("x" (lambda ()
(transient--make-description
"Censor"
censor-mode))
censor-mode)
("X" (lambda ()
(transient--make-description
"Censor (global)"
global-censor-mode))
global-censor-mode)
]
]
)
(global-set-key (kbd "C-c l v") #'transient/visual)
Transient for window management.
;; `next-multiframe-window' & `previous-multiframe-window' renamed to
;; `next-window-any-frame' & `previous-window-any-frame' in Emacs 27
(when (version< emacs-version "27")
(defalias 'next-window-any-frame 'next-multiframe-window)
(defalias 'previous-window-any-frame 'previous-multiframe-window))
(defun transient/window--transpose-windows (selector)
"Call SELECTOR and transpose buffers between current and selected windows."
(let ((from-win (selected-window))
(from-buf (window-buffer)))
(funcall selector)
(set-window-buffer from-win (window-buffer))
(set-window-buffer (selected-window) from-buf)))
(defun transient/window--transpose-window-up ()
"Transpose buffers between current and the window above it."
(interactive)
(transient/window--transpose-windows 'windmove-up))
(defun transient/window--transpose-window-down ()
"Transpose buffers between current and the window below it."
(interactive)
(transient/window--transpose-windows 'windmove-down))
(defun transient/window--transpose-window-left ()
"Transpose buffers between current and the window to its left."
(interactive)
(transient/window--transpose-windows 'windmove-left))
(defun transient/window--transpose-window-right ()
"Transpose buffers between current and the window to its right."
(interactive)
(transient/window--transpose-windows 'windmove-right))
;; add transient popup for window commands
(transient-define-prefix transient/window ()
"Window management commands."
:transient-suffix 'transient--do-stay
["Window"
["Navigate"
("n" "Next" next-window-any-frame)
("p" "Previous" previous-window-any-frame)
("o" "Other" other-window)
("<up>" "↑" windmove-up)
("<down>" "↓" windmove-down)
("<left>" "←" windmove-left)
("<right>" "→" windmove-right)
]
["Transpose"
("S-<up>" "↑" transient/window--transpose-window-up)
("S-<down>" "↓" transient/window--transpose-window-down)
("S-<left>" "←" transient/window--transpose-window-left)
("S-<right>" "→" transient/window--transpose-window-right)
("[" "Rotate bwd" my-rotate-buffers-backward)
("]" "Rotate fwd" my-rotate-buffers-forward)
]
["Layout"
("0" "Delete window" delete-window)
("1" "Delete other windows" delete-other-windows)
("2" "Split horiz" split-window-right)
("3" "Split vert" split-window-below)
("4" "Kill buffer and window" kill-buffer-and-window)
("u" "Winner undo" winner-undo)
("r" "Winner redo" winner-redo)
]
["Resize"
("-" "Shrink vert" shrink-window)
("^" "Enlarge vert" enlarge-window)
("{" "Shrink horiz" shrink-window-horizontally)
("}" "Enlarge horiz" enlarge-window-horizontally)
("M" "Maximize" maximize-window)
("m" "Minimize" minimize-window)
("+" "Balance" balance-windows)
]
]
)
(global-set-key (kbd "C-c e w") #'transient/window)
Transient for workspace manipulation and usage.
;; add transient popup for workspace commands
(transient-define-prefix transient/workspace ()
"Various workspace commands."
["Workspace"
["Desktop"
("dc" "Clear" desktop-clear)
("ds" "Save" desktop-save)
("dr" "Read" desktop-read)
("dR" "Revert" desktop-revert)
("dd" "Change Dir" desktop-change-dir)
]
["Tabs"
("<backtab>" "Previous" tab-bar-switch-to-prev-tab :transient t)
("<tab>" "Next" tab-bar-switch-to-next-tab :transient t)
("1" "Only" tab-bar-close-other-tabs :transient t)
("2" "New" tab-bar-new-tab :transient t)
("0" "Close" tab-bar-close-tab :transient t)
("u" "Undo close" tab-bar-undo-close-tab :transient t)
("r" "Rename" tab-bar-rename-tab :transient t)
("m" "Move" tab-bar-move-tab :transient t)
("M" "Move (frame)" tab-bar-move-tab-to-frame :transient t)
("<return>" "Switch" tab-bar-switch-to-tab)
]
]
)
(global-set-key (kbd "C-c e W") #'transient/workspace)
Transient for accessing writing-related commands.
;; add transient popup for writing commands
(require 'dictionary)
(require 'typo)
(defun transient/writing--ispell-dwim ()
"Dispatch to different Ispell spelling correction commands by major mode.
If the major mode derives from `prog-mode', call interactively
`ispell-comments-and-strings'.
If the major mode derives from `message-mode', call interactively
`ispell-message'.
Otherwise call interactively `ispell'.
Note that `ispell-comments-and-strings' and `ispell-message' do
not support restricting to a region."
(interactive)
(let ((fun (cond
((derived-mode-p 'prog-mode) #'ispell-comments-and-strings)
((derived-mode-p 'message-mode) #'ispell-message)
(t #'ispell))))
(call-interactively fun)))
(transient-define-prefix transient/writing ()
"Writing commands."
["Writing"
["Spelling"
("F" (lambda ()
(interactive)
(transient--make-description "Flyspell mode"
flyspell-mode))
flyspell-mode :transient t)
("P" "Flyspell prog mode" flyspell-prog-mode :transient t)
("B" "Check buffer" flyspell-buffer :transient t)
("n" "Next error" flyspell-goto-next-error :transient t)
("c" "Correct word" ispell-word :transient t)
("C" "Correct buffer/region" transient/writing--ispell-dwim :transient t)
("D" "Change dictionary" ispell-change-dictionary :transient t)
]
["Dictionary"
("ds" "Search" dictionary-search)
("dm" "Match words" dictionary-match-words)
]
["Typography"
("y" (lambda ()
(interactive)
(transient--make-description "Typography mode"
typo-mode))
typo-mode :transient t)]
]
)
(global-set-key (kbd "C-c l w") #'transient/writing)
Major mode transients are always bound to C-c m
and that binding
is active only when their corresponding major mode is active.
Debugger major mode transient.
;; major-mode specific transient for debugger-mode
(with-eval-after-load 'debug
(transient-define-prefix transient/debugger-mode ()
"`debugger-mode' commands."
["Emacs debugger"
["Movement"
("n" "Next line" next-line :transient t)
("p" "Previous line" previous-line :transient t)
]
["Breakpoints"
("b" "Set" debugger-frame)
("u" "Unset" debugger-frame-clear)
]
["Evaluate"
("e" "Sexp" debugger-eval-expression)
("R" "Sexp and record" debugger-record-expression)
]
]
[
[
"Stepping"
("d" "Step through" debugger-step-through)
("c" "Continue" debugger-continue)
("j" "Jump" debugger-jump)
("q" "Exit" top-level)
]
["Other"
("RET" "Follow at point" backtrace-help-follow-symbol)
("r" "Specify return value" debugger-return-value)
("l" "List debug functions" debugger-list-functions)
("v" "Toggle locals" backtrace-toggle-locals)
("h" "Help" describe-mode)
]
]
)
(define-key debugger-mode-map (kbd "C-c m") #'transient/debugger-mode))
Dired major mode transient.
;; major-mode specific transient for dired-mode
(with-eval-after-load 'dired
(defun transient/dired-mode--dired-kill-and-next-subdir ()
"Kill current subdir in dired, and jump back to its parent dir."
(interactive)
(let* ((subdir-name (directory-file-name (dired-current-directory)))
(parent-dir (file-name-directory subdir-name))
(search-term (concat " "
(file-name-base subdir-name)
(file-name-extension subdir-name t))))
(dired-kill-subdir)
(dired-goto-subdir parent-dir)
(search-forward search-term)))
(transient-define-prefix transient/dired-mode ()
"`dired-mode' commands."
["Dired"
["File open"
("RET" "Open" dired-find-file)
("o" "Open other" dired-find-file-other-window)
("F" "Open marked" dired-do-find-marked-files)
("z" "Open external" dired--open-file-at-pt)
("v" "View file" dired-view-file)
("+" "Create dir" dired-create-directory)
("=" "Diff" dired-diff)
]
["File operations"
("C" "Copy" dired-do-copy)
("D" "Delete" dired-do-delete)
("x" "Delete marked" dired-do-flagged-delete)
("S" "Symlink" dired-do-symlink)
("Y" "Symlink to" dired-do-relsymlink)
("c" "Compress to" dired-do-compress-to)
("Z" "Compress" dired-do-compress)
]
["File modification"
("R" "Rename" dired-do-rename)
("%R" "Rename by regexp" dired-do-rename-regexp)
("G" "chgrp" dired-do-chgrp)
("M" "chmod" dired-do-chmod)
("O" "chown" dired-do-chown)
("T" "Touch" dired-do-touch)
]
["Mark"
("m" "File at pt" dired-mark :transient t)
("E" "By extension" dired-mark-extension :transient t)
("t" "Toggle marks" dired-toggle-marks :transient t)
("u" "Unmark" dired-unmark :transient t)
("U" "Unmark all" dired-unmark-all-marks :transient t)
]
]
[
["Search/Filter"
("A" "Query" dired-do-find-regexp)
("Q" "Query-replace" dired-do-find-regexp-and-replace)
("{" "Find by name" find-name-dired)
("}" "Find by query" find-grep-dired)
]
["View"
("(" "Toggle details" dired-hide-details-mode :transient t)
(")" "Toggle omit" dired-omit-mode :transient t)
("i" "Insert subdir" dired-maybe-insert-subdir :transient t)
("K" "Kill subdir" transient/dired-mode--dired-kill-and-next-subdir :transient t)
("s" "Sort by date" dired-sort-toggle-or-edit :transient t)
]
["Other"
("y" "Show file type" dired-show-file-type :transient t)
("g" "Refresh" revert-buffer :transient t)
("l" "Redisplay" dired-do-redisplay :transient t)
("C-o" "Display other" dired-display-file)
("h" "Help" describe-mode)
]
]
)
(define-key dired-mode-map (kbd "C-c m") #'transient/dired-mode))
Edebug major mode transient.
;; major-mode specific transient for edebug-mode
(with-eval-after-load 'edebug
(transient-define-prefix transient/edebug-mode ()
"`edebug-mode' commands."
["Edebug"
["Modes"
("SPC" "Step" edebug-step-mode)
("n" "Next" edebug-next-mode)
("g" "Go" edebug-go-mode)
("G" "Go (nonstop)" edebug-Go-nonstop-mode)
("t" "Trace" edebug-Trace-fast-mode)
("c" "Continue" edebug-continue-mode)
("C" "Continue (fast)" edebug-Continue-fast-mode)
]
["Stepping"
("f" "Forward sexp" edebug-forward-sexp)
("h" "Continue to here" edebug-goto-here)
("I" "Instrument callee" edebug-instrument-callee)
("i" "Step in" edebug-step-in)
("o" "Step out" edebug-step-out)
]
["Breakpoints"
("b" "Set" edebug-set-breakpoint)
("u" "Unset" edebug-unset-breakpoint)
("B" "Next" edebug-next-breakpoint)
("x" "Set (cond-at-pt)" edebug-set-conditional-breakpoint)
("X" "Set (global cond)" edebug-set-global-break-condition)
]
]
[
["Evaluation"
("r" "Previous result" edebug-previous-result)
("e" "Sexp" edebug-eval-expression)
("C-e" "Last sexp" edebug-eval-last-sexp)
("E" "Visit eval list" edebug-visit-eval-list)
]
["Views"
("v" "Outside" edebug-view-outside)
("w" "Where" edebug-where)
("p" "Bounce point" edebug-bounce-point)
("W" "Toggle save windows" edebug-toggle-save-windows)
]
["Quitting/Stopping"
("q" "Top level" top-level)
("Q" "Top level (nonstop)" edebug-top-level-nonstop)
("a" "Abort recursive edit" abort-recursive-edit)
("S" "Stop" edebug-stop)
]
]
[
["Other"
("d" "Backtrace" edebug-pop-to-backtrace)
("=" "Frequency count" edebug-temp-display-freq-count)
("?" "Help" edebug-help)
]
]
)
(define-key edebug-mode-map (kbd "C-c m") #'transient/edebug-mode))
Emacs Web Wowser major mode transient.
;; major-mode specific transient for eww-mode
(with-eval-after-load 'eww
(transient-define-prefix transient/eww-mode ()
"`eww-mode' commands."
["Emacs Web Wowser"
["Navigation"
("G" "Search" eww)
("o" "Open file" eww-open-file)
("l" "Back" eww-back-url)
("r" "Forward" eww-forward-url)
("g" "Reload" eww-reload)
("s" "Switchb" eww-switch-to-buffer)
("S" "Buffers" eww-list-buffers)
("H" "History" eww-list-histories)
]
["Bookmarks"
("b" "Add" eww-add-bookmark)
("B" "List" eww-list-bookmarks)
]
["Toggle"
("F" "Fonts" eww-toggle-fonts :transient t)
("I" "Images" eww--toggle-images :transient t)
("M-C" "Colors" eww-toggle-colors :transient t)
("D" "Text direction" eww-toggle-paragraph-direction :transient t)
]
["Other"
("d" "Downlink link" eww-download)
("w" "Copy url" eww-copy-page-url)
("R" "View readable" eww-readable)
("v" "View source" eww-view-source)
("C" "Cookies" url-cookie-list)
("&" "Open external" eww-browse-with-external-browser)
]
]
)
(define-key eww-mode-map (kbd "C-c m") #'transient/eww-mode))
Ibuffer major mode transient.
(require 'ibuffer)
(transient-define-prefix transient/ibuffer-mode/mark ()
"`ibuffer-mode' mark commands."
:transient-suffix 'transient--do-stay
["Ibuffer → Mark"
[("*" "Unmark all" ibuffer-unmark-all)
("M" "By mode" ibuffer-mark-by-mode)
("m" "Modified" ibuffer-mark-modified-buffers)
("u" "Unsaved" ibuffer-mark-unsaved-buffers)
]
[("s" "Special" ibuffer-mark-special-buffers)
("r" "Read-only" ibuffer-mark-read-only-buffers)
("/" "Dired" ibuffer-mark-dired-buffers)
("e" "Disassociated" ibuffer-mark-dissociated-buffers)
]
[("h" "Help" ibuffer-mark-help-buffers)
("z" "Compressed" ibuffer-mark-compressed-file-buffers)
]
]
)
(transient-define-prefix transient/ibuffer-mode/action ()
"`ibuffer-mode' action commands."
["Ibuffer → Action"
["Run"
("E" "Eval in buffers" ibuffer-do-eval)
("W" "View buffers and eval" ibuffer-do-view-and-eval)
("F" "Command on files" ibuffer-do-shell-command-file)
("X" "Pipe to command" ibuffer-do-shell-command-pipe)
("N" "Pipe to command and replace" ibuffer-do-shell-command-pipe-replace)
]
["Search"
("O" "Occur" ibuffer-do-occur)
("U" "Replace regexp" ibuffer-do-replace-regexp)
("Q" "Query/Replace" ibuffer-do-query-replace)
("I" "Query/Replace regexp" ibuffer-do-query-replace-regexp)
]
["Properties"
("R" "Rename uniquely" ibuffer-do-rename-uniquely)
("M" "Toggle modified" ibuffer-do-toggle-modified)
("T" "Toggle read-only" ibuffer-do-toggle-read-only)
]
]
[
["Other"
("A" "View" ibuffer-do-view)
("H" "View (other)" ibuffer-do-view-other-frame)
("V" "Revert" ibuffer-do-revert)
("P" "Print" ibuffer-do-print)
]
]
)
(transient-define-prefix transient/ibuffer-mode/sort ()
"`ibuffer-mode' sort commands."
:transient-suffix 'transient--do-stay
["Ibuffer → Sort"
[("a" "Alphabetic" ibuffer-do-sort-by-alphabetic)
("f" "Filename/Process" ibuffer-do-sort-by-filename/process)
("m" "Mode" ibuffer-do-sort-by-major-mode)
]
[("s" "Size" ibuffer-do-sort-by-size)
("v" "Recency" ibuffer-do-sort-by-recency)
("i" "Invert" ibuffer-invert-sorting)
]
]
)
(transient-define-prefix transient/ibuffer-mode/filter ()
"`ibuffer-mode' filter commands."
:transient-suffix 'transient--do-stay
["Ibuffer → Filter"
["Predicates"
("a" "Add saved" ibuffer-add-saved-filters)
("c" "By content" ibuffer-filter-by-content)
("e" "By predicate" ibuffer-filter-by-predicate)
("f" "By filename" ibuffer-filter-by-filename)
("m" "By mode" ibuffer-filter-by-used-mode)
("M" "By derived mode" ibuffer-filter-by-derived-mode)
("n" "By name" ibuffer-filter-by-name)
(">" "By size gt" ibuffer-filter-by-size-gt)
("<" "By size lt" ibuffer-filter-by-size-lt)
]
["Operators"
("&" "AND" ibuffer-and-filter)
("|" "OR" ibuffer-or-filter)]
["Stack"
("p" "Pop" ibuffer-pop-filter)
("\\" "Clear" ibuffer-clear-filter-groups)
]
["Presets"
("R" "Saved" ibuffer-switch-to-saved-filter-groups)
("/" "Disable" ibuffer-filter-disable)
]
]
)
(defun transient/ibuffer-mode--activate-dwim ()
"Toggle filter group or visit buffer under point in `ibuffer-mode'."
(interactive)
(condition-case nil
(ibuffer-toggle-filter-group)
(error (ibuffer-visit-buffer))))
;; major-mode specific transient for ibuffer-mode
(transient-define-prefix transient/ibuffer-mode ()
"`ibuffer-mode' commands."
:transient-suffix 'transient--do-stay
["Ibuffer"
["Navigation"
("n" "Next line" ibuffer-forward-line)
("p" "Previous line" ibuffer-backward-line)
("RET" "Open" transient/ibuffer-mode--activate-dwim :transient nil)
("o" "Open (other)" ibuffer-visit-buffer-other-window :transient nil)
]
["Actions"
("m" "Mark" ibuffer-mark-forward)
("u" "Unmark" ibuffer-unmark-forward)
("*" "→ Mark" transient/ibuffer-mode/mark)
("S" "Save" ibuffer-do-save)
("D" "Delete" ibuffer-do-delete)
("a" "→ Action" transient/ibuffer-mode/action)
]
["View"
("`" "Switch format" ibuffer-switch-format)
("g" "Refresh" ibuffer-update)
("s" "→ Sort" transient/ibuffer-mode/sort)
("/" "→ Filter" transient/ibuffer-mode/filter)
]
]
)
(define-key ibuffer-mode-map (kbd "C-c m") #'transient/ibuffer-mode)
Markdown major mode transient.
;; major-mode specific transient for markdown-mode
(with-eval-after-load 'markdown-mode
(with-eval-after-load 'markdown-toc
(transient-define-prefix transient/markdown-mode ()
"`markdown-mode' commands."
:transient-suffix 'transient--do-stay
["Markdown mode (other bindings: 'C-c C-c', 'C-c C-s', 'C-c C-x')"
["Navigate"
("n" "Next" markdown-outline-next)
("p" "Previous" markdown-outline-previous)
("f" "Next (same level)" markdown-outline-next-same-level)
("b" "Previous (same level)" markdown-outline-previous-same-level)
]
["Move outline"
("<left>" "Promote" markdown-promote)
("<right>" "Demote" markdown-demote)
("<up>" "Move up" markdown-move-up)
("<down>" "Move down" markdown-move-down)
]
["Shift region"
("<" "Outdent" markdown-outdent-region)
(">" "Indent" markdown-indent-region)
]
]
[
["User interface"
("E" "Toggle math" markdown-toggle-math)
("F" "Toggle code font" markdown-toggle-fontify-code-blocks-natively)
("I" "Toggle images" markdown-toggle-inline-images)
("L" "Toggle show URL" markdown-toggle-url-hiding)
("M" "Toggle show markup" markdown-toggle-markup-hiding)
]
["Table of contents"
("t" "Insert/Refresh" markdown-toc-generate-or-refresh-toc :transient nil)
("C-t" "Delete" markdown-toc-delete-toc)
]
["Other"
("d" "Do" markdown-do :transient nil)
("o" "Follow" markdown-follow-thing-at-point :transient nil)
("N" "Cleanup list nums" markdown-cleanup-list-numbers :transient nil)
("'" "Edit code block" markdown-edit-code-block :transient nil)
]
]
)
(define-key gfm-mode-map (kbd "C-c m") #'transient/markdown-mode)
(define-key markdown-mode-map (kbd "C-c m") #'transient/markdown-mode)))
Org-agenda major mode transient.
;; major-mode specific transient for org-agenda-mode
(with-eval-after-load 'org-agenda
(defun transient/org-agenda-mode--hide-done ()
"Hide items with DONE state in `org-agenda-mode' buffer."
(interactive)
(setq org-agenda-skip-scheduled-if-done
(not org-agenda-skip-scheduled-if-done))
(org-agenda-redo-all t))
(transient-define-prefix transient/org-agenda-mode ()
"`org-agenda-mode' commands."
:transient-suffix 'transient--do-stay
["Org agenda"
["Agenda view"
("d" "Day" org-agenda-day-view)
("w" "Week" org-agenda-week-view)
("f" "Later" org-agenda-later)
("b" "Earlier" org-agenda-earlier)
]
["Navigate"
("n" "Next line" org-agenda-next-line)
("p" "Prev line" org-agenda-previous-line)
("N" "Next item" org-agenda-next-item)
("P" "Prev item" org-agenda-previous-item)
]
["Visit"
("SPC" "Show" org-agenda-show-and-scroll-up :transient nil)
("TAB" "Goto" org-agenda-goto :transient nil)
("RET" "Switch to" org-agenda-switch-to :transient nil)
("C-c C-o" "Link" org-agenda-open-link :transient nil)
]
["Other"
("r" "Redisplay" org-agenda-redo)
("j" "Goto date" org-agenda-goto-date)
("." "Goto today" org-agenda-goto-today)
("(" (lambda ()
(transient--make-description
"Hide DONE"
org-agenda-skip-scheduled-if-done))
transient/org-agenda-mode--hide-done)
]
]
[
["Filter"
("<" "By category" org-agenda-filter-by-category)
("_" "By effort" org-agenda-filter-by-effort)
("=" "By regexp" org-agenda-filter-by-regexp)
("\\" "By tag" org-agenda-filter-by-tag)
("^" "By top headline" org-agenda-filter-by-top-headline)
("|" "Remove all" org-agenda-filter-remove-all)
]
["Clock"
("I" "In" org-agenda-clock-in)
("O" "Out" org-agenda-clock-out)
("X" "Cancel" org-agenda-clock-cancel)
("J" "Current task" org-agenda-clock-goto)
("R" (lambda ()
(transient--make-description
"Clocktable"
org-agenda-clockreport-mode))
org-agenda-clockreport-mode)
]
["Modify"
("t" "Status" org-agenda-todo)
(":" "Tags" org-agenda-set-tags)
("," "Priority" org-agenda-priority)
("z" "Add note" org-agenda-add-note)
("C-c C-x p" "Property" org-agenda-set-property)
]
]
[
["Date"
(">" "Prompt" org-agenda-date-prompt)
("C-c C-s" "Schedule" org-agenda-schedule)
("C-c C-d" "Deadline" org-agenda-deadline)
]
["Node ops"
("$" "Archive" org-agenda-archive)
("C-c C-w" "Refile" org-agenda-refile)
("C-k" "Kill" org-agenda-kill)
]
]
)
(define-key org-agenda-mode-map (kbd "C-c m") #'transient/org-agenda-mode))
Org major mode transient.
;; major-mode specific transient for org-mode
(with-eval-after-load 'org
(defun transient/org-mode--toggle-display-image-width ()
"Toggle resizing of inline images in `org-mode' to one-third screen width."
(interactive)
(if org-image-actual-width
(setq org-image-actual-width nil)
(setq org-image-actual-width (list (/ (display-pixel-width) 3))))
(org-redisplay-inline-images))
(defun transient/org-mode--next-heading-dwim (n)
"Go to N-th next occur highlight or visible heading otherwise."
(interactive "p")
(if org-occur-highlights
(next-error n)
(org-next-visible-heading n)))
(defun transient/org-mode--previous-heading-dwim (n)
"Go to N-th previous occur highlight or visible heading otherwise."
(interactive "p")
(if org-occur-highlights
(previous-error n)
(org-previous-visible-heading n)))
(defun transient/org-mode--org-insert-structure-template ()
"Wrapper for `org-insert-structure-template' so `transient/org-mode' can work with Org 9.3."
(interactive)
(if (fboundp 'org-insert-structure-template)
(call-interactively #'org-insert-structure-template)
(message (concat "Current version of Org does not define `org-insert-structure-template'."
"\n"
"Use the '<{char}' (e.g. '<s') structure expansions instead."))))
(transient-define-prefix transient/org-mode ()
"`org-mode' commands."
["Org"
["Toggle"
("i" (lambda ()
(transient--make-description
"Images"
org-inline-image-overlays))
org-toggle-inline-images :transient t)
("I" (lambda ()
(transient--make-description
"Indent"
org-indent-mode))
org-indent-mode :transient t)
("P" (lambda ()
(transient--make-description
"Prettify entities"
org-pretty-entities))
org-toggle-pretty-entities :transient t)
("M-l" (lambda ()
(transient--make-description
"Link display"
(if (version< org-version "9.3")
(not org-descriptive-links)
(not org-link-descriptive))))
org-toggle-link-display :transient t)
("M-i" (lambda ()
(transient--make-description
"Image resize"
org-image-actual-width))
transient/org-mode--toggle-display-image-width :transient t)
]
["Search"
("g" "Goto" org-goto)
("o" "Occur" org-occur :transient t)
("/" "Create sparse tree" org-sparse-tree :transient t)
("c" "Clear search results" org-remove-occur-highlights :transient t)
("n" "Next (sparse) node" transient/org-mode--next-heading-dwim :transient t)
("p" "Previous (sparse) node" transient/org-mode--previous-heading-dwim :transient t)
]
["Modify"
("t" "Todo state" org-todo)
(":" "Tags" org-set-tags-command)
("," "Priority" org-priority)
("D" "Insert drawer" org-insert-drawer)
("P" "Set property" org-set-property)
("N" "Add note" org-add-note)
]
]
[
["Node ops"
("a" "Archive" org-archive-subtree-default)
("r" "Refile" org-refile)
("s" "Sort" org-sort)
]
["Text ops"
("F" "Add footnote" org-footnote-action)
("<" "Insert structure" transient/org-mode--org-insert-structure-template)
("'" "Edit special" org-edit-special)
("e" "Emphasize" org-emphasize)
]
[:description (lambda ()
(transient--make-description
"Narrow"
(buffer-narrowed-p)))
("M-s" "Subtree" org-narrow-to-subtree)
("M-b" "Block" org-narrow-to-block)
("M-w" "Widen" widen)
]
["Other"
("<tab>" "Cycle node" org-cycle :transient t)
("<S-tab>" "Cycle global" org-global-cycle :transient t)
]
]
)
(define-key org-mode-map (kbd "C-c m") #'transient/org-mode))
Smerge is a lightweight alternative to Ediff, and is the default merge tool called by Magit to resolve merge conflicts.
This is a transient for the smerge-mode
major mode that is adapted
from here.
;; major-mode specific transient for smerge-mode
(with-eval-after-load 'smerge-mode
(transient-define-prefix transient/smerge-mode ()
"`smerge-mode' commands."
;; have suffixes not exit the transient by default
:transient-suffix 'transient--do-stay
["Smerge"
["Move"
("n" "Next" smerge-next)
("p" "Previous" smerge-prev)]
[
"Keep"
("b" "Base" smerge-keep-base)
("u" "Upper" smerge-keep-upper)
("l" "Lower" smerge-keep-lower)
("a" "All" smerge-keep-all)
("RET" "Current" smerge-keep-current)
]
["Diff"
("<" "Upper/Base" smerge-diff-base-upper)
("=" "Upper/Lower" smerge-diff-upper-lower)
(">" "Base/Lower" smerge-diff-base-lower)
("R" "Refine" smerge-refine)
("E" "Ediff" smerge-ediff)
]
[
"Other"
("C" "Combine" smerge-combine-with-next)
("r" "Resolve" smerge-resolve)
("k" "Kill current" smerge-kill-current)
;; emulate Vim's "ZZ" command to save and close current file
("ZZ" "Save and bury buffer" my-save-and-bury-buffer
:transient nil)
]
]
)
(define-key smerge-mode-map (kbd "C-c m") #'transient/smerge-mode))
Term major mode transient.
This is mainly to toggling between term-char-mode
(which is similar
to a regular terminal emulator) and term-line-mode
(which is similar
to Shell mode) when in term-mode.
;; major-mode specific transient for term-mode
(with-eval-after-load 'term
(defun transient/term-mode--toggle-char-mode-line-mode ()
"Toggle between `term-char-mode' and `term-line-mode' in `term-mode'."
(interactive)
(if (term-in-line-mode)
(progn (term-char-mode) (message "line → char"))
(progn (term-line-mode) (message "char → line"))))
(transient-define-prefix transient/term-mode ()
"`term-mode' commands."
["Term"
("m" "Toggle between `term-char-mode' and `term-line-mode'"
transient/term-mode--toggle-char-mode-line-mode :transient t)
]
)
(define-key term-mode-map (kbd "C-c m") #'transient/term-mode)
(define-key term-raw-map (kbd "C-c m") #'transient/term-mode))
Minor mode transients are bound to a binding other than C-c m
(which is reserved for major mode transients). These typically have
global bindings to allow toggling of the minor mode in buffers.
Flymake minor mode transient.
;; add transient for Flymake
(with-eval-after-load 'flymake
(transient-define-prefix transient/flymake-mode ()
"`flymake-mode' commands."
:transient-suffix 'transient--do-stay
[:description (lambda ()
(transient--make-description
"Flymake"
flymake-mode))
["Error"
("n" "Next" flymake-goto-next-error)
("p" "Previous" flymake-goto-prev-error)
("l" "List" my-toggle-flymake-diagnostics)
("." "Describe" display-local-help)
]
["Check"
("c" "Start" flymake-start)
("k" "Stop" flymake-proc-stop-all-syntax-checks)
]
["Other"
("m" "Toggle mode" flymake-mode)
("r" "Reporting backends" flymake-reporting-backends)
("d" "Disabled backends" flymake-disabled-backends)
("l" "Log" flymake-switch-to-log-buffer)
("c" "Compile (no check)" flymake-proc-compile)
]
]
)
(global-set-key (kbd "C-c F") #'transient/flymake-mode))
(concat "(provide '" feature ")\n"
";;; " feature ".el ends here")
The main configuration file.
<<generate-header(feature="init", summary="Emacs init file")>>
<<author-info>>
<<generate-timestamp()>>
;;; Commentary:
<<file-commentary-init>>
;;; Code:
;; Package management
<<load-prefer-newer>>
<<add-dirs-to-load-path>>
;; Visual user interface components
<<cursor>>
<<hide-cursor-in-non-selected-windows>>
<<remove-decorations>>
;; Customize file and local configuration
<<custom-file-and-init-local>>
;; Custom variables and utility functions / Custom variables
<<my-system-open-command>>
;; Custom variables and utility functions / Utility functions
<<my-after-jump-context-actions>>
<<my-save-and-bury-buffer>>
<<my-install-elpa-package>>
;; Package management
<<elpa-repositories>>
<<package-init>>
<<use-package>>
<<my-package-reinstall>>
;; Backend and frontend frameworks for building user interfaces
<<edit-indirect>>
<<company>>
;; Visual (part 1)
;; Backups
<<backup-files-directory>>
;; Bookmarks and history
<<recentf>>
<<saveplace>>
<<savehist>>
;; Buffers, windows, frames, workspaces / Buffer management
<<protect-buffers>>
<<ibuffer>>
<<ibuffer-filter-groups>>
<<buffer-expose>>
<<revert-without-query-mode>>
;; Buffers, windows, frames, workspaces / Window management
<<winner-mode>>
<<more-convenient-other-window-binding>>
<<my-rotate-window-buffers>>
<<help-window-select>>
;; Buffers, windows, frames, workspaces / Frame management
<<frame-resize-pixelwise>>
<<transpose-frame>>
<<more-convenient-other-frame-binding>>
;; Buffers, windows, frames, workspaces / Workspace management
<<desktop>>
<<tab-bar-mode>>
;; Command-line interaction
<<eshell>>
<<eshell-visual-commands>>
<<eshell-disable-git-pager>>
<<eshell-named-buffers>>
<<eshell-bookmark>>
<<comint-prompt-read-only>>
<<kill-term-buffers-with-q-after-end>>
<<tmux-send>>
;; Comparison tools
<<ediff>>
<<ediff-copy-a-and-b-to-c>>
;; Dired
<<dired>>
<<dired-open-file-at-pt>>
;; Editing text
<<indent-with-soft-tabs>>
<<completing-yank>>
<<delsel>>
<<sentence-end-single-space>>
<<epa-file>>
<<iedit>>
<<multiple-cursors>>
<<paredit>>
<<undo-tree>>
<<zap-up-to-char>>
<<cycle-spacing>>
<<my-join-next-line>>
<<my-open-line-below-and-above>>
<<bind-dwim-editing-commands>>
;; Emacs as an edit server
<<sigusr1-restart-emacs-server>>
;; Non-programming files
<<markdown-mode>>
<<markdown-toc>>
<<yaml-mode>>
;; Org-mode
;; <<org-elpa>>
<<rebind-org-force-cycle-archived-when-using-older-org-versions>>
<<org-base-settings>>
<<my-org-open-line-below>>
<<org-todo-keywords>>
<<org-capture-templates>>
<<org-maximize-capture-buffers>>
<<org-tags>>
<<org-export-global-macros>>
<<org-file-apps>>
<<org-agenda>>
<<org-refile>>
<<org-visual-line-mode>>
<<org-latex-pdf-process>>
<<ox-md>>
<<org-tempo>>
;; Programming / Flymake syntax checker
<<flymake>>
;; Programming / Emacs Lisp
<<el-patch>>
<<modify-lisp-indent-function>>
;; Programming / fish shell scripts
<<fish-mode>>
;; Search
<<wgrep>>
<<imenu-auto-rescan>>
<<imenu-list>>
<<search-default-mode>>
;; Visual (part 2)
<<display-line-numbers-when-editing-code>>
<<show-trailing-whitespace>>
<<show-column-number>>
<<show-matching-parentheses>>
<<add-frame-internal-border>>
<<censor>>
<<too-long-lines-mode>>
;; Web
<<eww>>
<<open-gnutls-stream-workaround>>
<<network-security>>
<<http-requests-privacy>>
;; Writing
<<flyspell-my-after-jump-context-actions-advice>>
<<flyspell-unbind-conflicting-bindings>>
<<dictionary>>
<<typo>>
;; Other
<<real-auto-save>>
<<scratch-buffer-initial-mode>>
<<scroll-conservatively>>
<<scroll-line-at-a-time>>
<<silence-audio-and-visual-bells>>
<<suppress-auto-revert-messages>>
<<suppress-startup-splash>>
<<vlf>>
<<so-long>>
<<uniquify>>
<<calc>>
<<enable-default-disabled-functions>>
<<revert-buffer>>
<<tramp-remote-search-paths>>
<<set-mark-command-repeat-pop>>
<<my-clear-register>>
<<xr>>
<<menu-bar-open-alternate-binding>>
;; Transient commands
<<transient>>
;; Transient commands / Global transients
<<bookmarks-transient>>
<<buffer-transient>>
<<debugger-transient>>
<<ediff-transient>>
<<edit-transient>>
<<frame-transient>>
<<help-transient>>
<<keyboard-macros-transient>>
<<marks-and-markers-transient>>
<<org-launcher-transient>>
<<package-transient>>
<<profiler-transient>>
<<registers-transient>>
<<search-transient>>
<<server-transient>>
<<shell-transient>>
<<system-transient>>
<<visual-transient>>
<<window-transient>>
<<workspace-transient>>
<<writing-transient>>
;; Transient commands / Major mode transients
<<debugger-mode-transient>>
<<dired-mode-transient>>
<<edebug-mode-transient>>
<<eww-mode-transient>>
<<ibuffer-mode-transient>>
<<markdown-mode-transient>>
<<org-agenda-mode-transient>>
<<org-mode-transient>>
<<restclient-mode-transient>>
<<smerge-mode-transient>>
<<term-mode-transient>>
;; Transient commands / Minor mode transients
<<flymake-mode-transient>>
<<generate-footer(feature="init")>>
Autocaption source blocks with their names when exporting so that noweb entries in the Tangled files section can be referenced against their source blocks.
Local Variables:
eval: (setq-local org-babel-exp-code-template (concat "\n#+caption:=%name=\n" org-babel-exp-code-template))
End: