A multi purpose Emacs configuration, appreciating the built-in features and exploring the future ones.
This configuration focuses on:
- Use of the built-in Emacs features, unless it’s inferior or missing.
- Fast startup time (0.05 seconds in TTY, 0.2 seconds in GUI)
- Default Emacs keys
This configuration has very fast startup times. It accomplishes this by maximizing the built-in features available to Emacs since version 28.1. So how is the fast start-up time achieved:
- Byte compilation of the Emacs configuration (make emacs)
- Utilise package.el to install external packages, but do not initialise package.el during runtime Emacs.
- Utilise use-package.el to configure features, but use expanded macros in byte-compiled files during runtime Emacs.
- Lazy loading of code, if you don’t need it - don’t load it
- Use builtin features of Emacs (who would have thought)
The result is that Emacs starts in a TTY in approx 0.05 seconds and Emacs graphical in 0.2 seconds.
More often than not are we attracted by eye candy extensions to Emacs, without considering what Emacs has to offer directly out-of-the-box. With each new version of Emacs, new features are included and it is worthwhile reviewing these before looking for alternatives. This configuration uses builtin features before external packages, unless there is a clear advantage over using an external alternative. It’s your choice to enable such alternatives by including the pure-future.el module, or run built-in features only by requiring pure-Emacs.
Note: The variable package–builtins defined in package.el lists all Emacs built-in features!
These built-in features are configured:
;;;; Emacs
;;;;; = emacs - sensible defaults
;;;;; = cus-edit - Emacs customization
;;;; Appearance
;;;;; = timer - display time
;;;;; = simple - modeline info and line display
;;;;; = display-fill-column-indicator
;;;;; = display-line-numbers
;;;;; = hl-line - highlight line at point
;;;; Help and Information
;;;;; = help - always select the help windows
;;;;; = eldoc - echo area context at point information
;;;;; = transient - menus and options
;;;;; = apropos - find symbols, functions, variables, etc
;;;; File Management
;;;;; = files - files and backups
;;;;; = recentf - recently opened files
;;;;; = saveplace - last position in file
;;;;; = dired - file management
;;;;; = uniquify - files with the same name get directory included.
;;;; Buffer Management
;;;;; = ibuffer - buffer management
;;;; Window Management
;;;;; = windmove - reposition buffers
;;;;; = winner - undo / redo window order
;;;; Minibuffer and Completion
;;;;; = minibuffer - selection window
;;;;; = savehist - last minibuffer commands used
;;;;; = icomplete - vertical completion buffer
;;;; Search and Replace
;;;;; = isearch - find
;;;;; = replace - contains occur
;;;; Keys
;;;;; - repeat - why repeat complex key combinations
;;;; Editing
;;;;; = ispell - spell checking (install hunspell)
;;;;; = flyspell - on the fly spell checking
;;;;; = dictionary - look up words for meaning (on-line)
;;;;; = abbrev - replace acronims with full word
;;;;; = dabbrev - dynamic abbreviations
;;;;; = delsel - delete selected
;;;; Navigation
;;;;; = imenu - list content of a buffer in headers
;;;; Coding
;;;;; = project - project management
;;;;; = vc - version control
;;;;; = outline - code folding
;;;;; = hideshow - function and expression folding
;;;;; = reveal - open folded blocks when searching
;;;;; = elec-pair - parenthesis
;;;;; = whitespace - show whitespaces
;;;;; = flymake - identify code faults
;;;;; = treesit - Emacs language parser
;;;;; = eglot - Emacs client for the Language Server Protocol
;;;; Programming Languages
;;;;; = python - programming in python
;;;; Shells
;;;;; = eshell - the Emacs shell created with elsip. Runs everywhere.
;;;; Tools
;;;;; = calendar - calendar dialogue.
;;;;; = url
;;;; Security and Privacy
;;;;; = epg - Emacs GnuPG interface
;;;;; = epa - EasyPG file encryption and decryption
;;;;; = auth-source - handle username / passwords for accounts
;;;;; = auto-source-pass
;;;; Communication
;;;;; = eww - Emacs web wowser
;;;; Org Mode
;;;;; = org-mode - the one and only writing environment (and more)
;;;; Pure Functions
;;;;; = pure--suppress-messages
;;; pure-emacs.el ends here
This configuration is modular. There are currently 5 modules of which two are compulsory:
- pure-common - variables you might want to personalize.
- pure-emacs - Emacs built-in features, good starting point for your Emacs setup.
- pure-future - Emacs external packages that are popular and potential candidates for future integration in core Emacs.
- pure-me - Emacs external packages that are of interest to me/you, because of your daily work
Some features in Emacs require external dependencies to be installed separately.Below is a list of dependencies by module:
- pure-emacs.el (vanilla Emacs):
- hunspell - spelling checker (or aspell, or ispell)
- hunspell-en_au - spelling checker language (change to your region)
- dict - dictionary application
- dict-gcide - dictionary (there are options such as dict-wn. -gcide, etc)
- gnupg - pretty good protection
- python-lsp-server - python language server
- (optional) pure-email (email client):
- mu - mail utilities that include an Emacs client (Elisp)
- mbsync - (or isync) mailbox synchronization
- msmtp - SMTP client with sendmail compatibility
It is assumed you have installed the latest version of Emacs using your OS package manager, and your Emacs version is at least 29.1 or later.
Use git to clone pure-emacs:
cd ~
git clone https://github.com/echjansen/pure-emacs ~/.emacs.d
This Emacs configuration focuses on fast startup and execution times. This can be achieved using the provided native compilation
introduced in Emacs 29.
Unlike many other configurations pure-emacs
utilities the built-in package manager package.el
to download external packages.
However, to maintain fast startup times Emacs features such as package.el
and use-package
are only loaded during compile time (make emacs
) but not during run time.
See more on this topic in Fast start-up time
To run this pure-emacs
configuration, the user must compile it after configuration modifications are made, with:
cd ~/.emacs.d
# Option 1 : Download packages and compile the configuration
make install
# Option 2 : Clean the compiled files to run ~uncompiled~ (slower)
make emacs
# Option 3 : Report on package load time
make report
This configuration works out of the box, however a few variables used with Emacs should be personalized. These parameters are found in the pure-common.el file and should be visited before running Emacs for the first time.
(defconst pure-dir-emacs (expand-file-name user-emacs-directory)
"The path to the emacs.d directory.")
;;;;; Pure Emacs modules directory
(defconst pure-dir-modules (concat user-emacs-directory "modules")
"The path to the emacs.d modules directory.")
;;;;; Pure Emacs cache diretcory (info shared between Emacs instances
(defconst pure-dir-cache "~/.cache/emacs/"
"The directory for Pure-Emacs littered files.")
;;;;; Pure Emacs backup directory
(defconst pure-dir-backup (concat pure-dir-cache "backup/")
"The directory for Pure-Emacs backup files.")
;;;;; Pure Emacs private directory
(defconst pure-dir-private "~/.config/emacs/"
"The directory for Pure-Emacs backup files.")
;;;;; Pure Emacs notes directory
(defconst pure-dir-notes "~/Projects/pure-notes/"
"The directory for Pure-Emacs note files directory.")
;;;;; Create directories if non existing
(dolist (dir (list pure-dir-cache
pure-dir-backup
pure-dir-notes
pure-dir-private))
(unless (file-directory-p dir)
(make-directory dir t)))
;;;; Files
;;;;; Pure Emacs custom file
;; Some variables may contain ~secret~ information
;; The custom file can store these variables securely by saving them as .gpg
(defconst pure-custom-file "~/.config/emacs/pure-custom.el.gpg"
"The custom file containing secrets")
Some of the pure-future module introduce interesting features that are found under key-bindings uncommon. Here is a list of key-bindings introduced with pure-Emacs:
C-h B | embark-bindings | Shows which-key like mini-buffer with keys available in current mode |
C-x C-h | Shows which-key like mini-buffer with C-x key options available in current mode | |
C-c C-h | Shows which-key like mini-buffer with C-c key options available in current mode | |
C-c . | embark-act | Prompts user for action on object under cursor (like the right-mouse click) |
C-c ; | embark-dwim | Run the default action on the current target |
S-left | windmove-left | Move focus to buffer to the left |
S-right | windmove-right | Move focus to buffer to the right |
S-up | windmove-up | Move focus to buffer above |
S-down | windmove-down | Move focus to buffer below |
C-left | windmove-swap-states-left | Swap current buffer with buffer to the left |
C-right | windmove-swap-states-right | Swap current buffer with buffer to the right |
C-up | windmove-swap-states-up | Swap current buffer with buffer above |
C-down | windmove-swap-states-down | Swap current buffer with buffer below |
C-, | flyspell-goto-next-error | Spell check goto next error |
C-. | flyspell-auto-correct-word | Spell check auto correct word |
TAB | outline-cycle | In Emacs-lisp-mode cycle outlines |
S-TAB | outline-cycle-buffer | In Emacs-lisp-mode cycle outline levels |
C-c i | eldoc | In code, provide Information about function, variable, etc |
C-c r | eglot-rename | In code, rename function, variable project wide |
C-c-f | eglot-format-buffer | In code, format code in current buffer |
Emacs has been around for a while. It is therefor not unimaginable that something about Emacs is in fact good and functional. New (and long term) Emacs users quite often install external packages, without fully understaning what vanilla Emacs has to offer. This configuration contains a configuration file that only facilitates the built-in features of the Emacs (currentl version 29+). To try pure-emacs, (after installation) execute the following shell command:
cd ~/.emacs.d/
emacs -Q -l early-init.el -l modules/pure-common.el -l modules/pure-emacs.el
This particular configuration on TTY starts (currently) in ~0.05 seconds (that is 50 ms) with all packages installed and configured. It is achieved with the built-in provided features:
- package.el (for package installation)
- use-package (for package configuration)
- byte-compile (for removing package.el and use-package from the start-up code)
- Not using auto-loads
Lets have a closer look at what is required to obtain a fast start-up time.
To get an idea of how much time is being added by loading the Emacs configuration one needs to know how much time Emacs takes to load by itself. This can easily be measured on a Linux system with the following command:
For the TTY version on Emacs (0.18 seconds):
time -p emacs -nw -Q -e kill-emacs
time -p emacs -Q -e kill-emacs
For the GUI version on Emacs (0.54 seconds): Notes Some useful Emacs flags are: -Q means to load no configuration (ignore init.el) -e execute a function -l load a file -nw start Emacs in TTY mode (no GUI)
Emacs comes with a built-in package installation tool called package.el. It takes car of:
- installing packages from remote repositories, and once installed.
- it takes care of maintaining the load-path requirements of the installed package and finally.
- it creates the package-autoloads.el file.
- it byte-compiles the package files.
It does more, but these are the important functions.
To use package.el it most be initialized with (package-initialize), so lets find out how much time is required to start the package manager by starting Emacs with the following minimal configuration:
;; Set sources for packages (recipes in this case)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "http://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
;; Initialise the package manager
(package-initialize)
Execution for the TTY goes up (0.18 -> 0.38):
time -p emacs -nw -batch -l init.el
That’s a doubling of the load time, or an additional 200 ms to the startup time! And this time becomes longer once more packages have been installed. Although package.el is required for the installation of packages, we can do without during runtime. There are two variables that control the loading of package.el:
;; Whether to make installed packages available when Emacs starts.
;; If non-nil, packages are made available before reading the init
;; file (but after reading the early init file). This means that if
;; you wish to set this variable, you must do so in the early init
;; file.
(setq package-enable-at-startup nil)
;; Some Emacs versions automatically insers (package-initialize in init.el)
;; Ask package.el to not add (package-initialize) to init.el.
(setq package--init-file-ensured t)
;; Or alternatively commen out (package-initialize) in the init.el file
;;(package-initialize)
Note that without package.el initialized, you can still install packages via M-x ‘package-list-packages’.
Note that without package.el initialized, the path’s to the packages are NOT added to ‘load-path’ and Emacs will NOT be able to locate and load the packages.
One the overcome the load-path issue, is to to manually code the load path of each package to the init.el, like this:
(setq package-enable-at-startup nil)
(setq package--init-file-ensured t)
(add-to-list 'load-path (concat package-user-dir "magit-20240101.1234"))
And this works perfectly, as Emacs looks trough all folders listed in ‘load-path’.
A simpler way is to automate the addition of the packages to ‘load-path with the code below, which is ONLY executed during compile time, and so the load-path is ‘hard-coded’ in the byte compiled file (hence the importance of byte compiling via the make command):
;; Traverse the installed packages and add their paths to load-path.
(mapc #'(lambda (add) (add-to-list 'load-path add))
(eval-when-compile
;; (require 'package)
(package-initialize)
(let ((package-user-dir-real (file-truename package-user-dir)))
;; The reverse is necessary, because outside we mapc
;; add-to-list element-by-element, which reverses.
(nreverse (apply #'nconc
;; Only keep package.el provided loadpaths.
(mapcar #'(lambda (path)
(if (string-prefix-p package-user-dir-real path)
(list path)
nil))
load-path))))))
Use-package is the excellent macro written by John Wiegley. It became part of core Emacs from version 29. The purpose of use-package is to put all configuration for a certain package tightly together within one expression. When configured correctly it also allows for faster start-up times by applying lazy loading - loading of a package only when it is required.
One additional - lesser known - fact is that use-package is not required to be loaded during run time when byte-compiled, as all the use-package expressions expand to standard lisp code (with some minimal overhead)!
All that is required for this to happen, is the following statement early in the configuration:
(eval-when-compile
(require 'use-package))
A seperate module is availale to include email in your Emacs configuration. It is disabled by default, as not to throw errors during the Emacs startup process, as the email configuration requires multiple personalisations before it can function correctly. Below is an outline of what need to be configured i order for email to work. Note: pure-dotfiles includes an install script that performs all of the necessary operations.
Pure-email assumes that all secrets regarding your email configuration are stored in a Gnu pass
password store.
The following is an example of a multi-line email account configuration.
<password>
email: <email>
name: <name>
imap-server:<imap.server.com>
imap-port:<port no>
imap-ssl:IMAPS
Pure-email uses instructions as below to obtain the data from the password store:
(user-full-name . ,(auth-source-pass-get "name" "email/user"))
Mailbox synchronization (receiving email) is performed by an external application, not mu4e (or mu)
There are two candidates for Synchronizing (receiving) mailboxes: mbsync
which is part of the isync
project, and Offlineimap
.
pure-email doesn’t care which utility is being used, as it solely functions on the offline mailbox and doesn’t perform the synchronization directly.
Alter the variable mu4e-get-mail-command
if not using mbsync
for mailbox synchronization.
(mu4e-get-mail-command "mbsync -a")
Assuming that mbsync is being used, a mbsynrc
configuration file is required in the users home folder.
The following is an example for outlook.com (or office365.com)
Replace user
with your email account identifier. Also, the block below can be duplicated multiple times for multiple email accounts
#===============================================================================
# Mailbox synchronisation with echjansen@outlook.com
#===============================================================================
IMAPAccount user
Host outlook.office365.com
Port 993
User user@outlook.com
PassCmd "pass email/user"
SSLType IMAPS
AuthMusers *
CertificateFile /etc/ssl/certs/ca-certificates.crt
IMAPStore user-remote
Account user
MaildirStore user-local
SubFolders Verbatim
Path ~/.mail/user/
Inbox ~/.mail/user/Inbox
Trash Deleted
Channel user
Far :user-remote:
Near :user-local:
Patterns *
SyncState *
Create Both
Expunge Both
CopyArrivalDate yes
Sync All
Like mailbox synchronization, sending emails is also performed by an external application. The common application is msmtp
.
msmtp
requires a .msmtprc
configuration file to be located in the users home folder.
The following is an example for outlook.com (or office365.com)
# Set default values for all following accounts.
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log
# echjansen@outlook.com
account user
host smtp-mail.outlook.com
port 587
from user@outlook.com
user user@outlook.com
passwordeval "pass email/user"
from_full_name "user full name"
The following configuration ensures that the data located in the .msmtprc
is being used.
(message-send-mail-function 'message-send-mail-with-sendmail)