161 KiB
Emacs Configuration
- Introduction
- Spacemacs layers and packages
- Init
- User Initialization
- User Configuration
- Custom functions
- Emacs builtins
- Editing and modes
- Keybindings
- Mu4e
- Miscellaneous
- Visual configuration
- Nov-mode
- Programming
- Projectile
- Security
- Snippets
- Yadm
- Footnotes
Introduction
This file is the main source file for my Emacs configuration which contains
most of the user code. It is exported thanks to Emacs’ code tangling from the
original Org file which you can find on my dotfiles’ repository1 if you
are reading the web version of it. You can also find there my
.spacemacs
2 and its code which isn’t part of the present file. As you
can see in my .spacemacs
, at init-time, if Emacs detects the tangled
configuration files are older than the Org file, then Emacs tangles them
again, and then loads them.
Spacemacs layers and packages
Here will be our layer configuration set. Everything here is set with a
setq-default
in the dotspacemacs/layers
function like so:
(defun dotspacemacs/layers ()
(setq-default
;; configuration goes here
))
General configuration
First, we need to tell Spacemacs which base distribution we are using. This
is a layer contained in the directory +distribution
. For now, available
distributions are spacemacs-base
and spacemacs
(the default one).
(setq-default dotspacemacs-distribution 'spacemacs)
We can seet a lazy installation of layers —i.e. layers are installed only when a file with a supported type is opened. Possible values are:
-
all
- will lazy install any layer that support lazy installation even
the layers listed in
dotspacemacs-configuration-layers
-
unused
- will lazy install only unused layers (i.e. layers not listed in
the variable
dotspacemacs-configuration-layers
) -
nil
- disables the lazy installation feature and you have to explicitly
list a layer in the variable
dotspacemacs-configuration-layers
to install it.
The default value is unused
.
(setq-default dotspacemacs-enable-lazy-installation 'unused)
If the following variable is non-nil, Spacemacs will ask for confirmation
before installing a layer lazily. The default value is t
.
(setq-default dotspacemacs-ask-for-lazy-installation t)
Package management
It is possible to indicate to Spacemacs a list of additional paths where to
look for configuration layers. Paths must have a trailing slash, i.e.
~/.mycontribs/
. As you can see, I added none:
(setq-default dotspacemacs-configuration-layer-path '())
However, I do have additional packages I installed either from the Elpa or
the Melpa. These are set in dotspacemacs-additional-packages
, a list of
additional packages that will be installed without being wrapped in a layer.
If you need some configuration for these packages, then consider creating a
layer. You can also puth the configuration in dotspacemacs/user-config
. To
use a local version of a package, use the :location
property, for instance:
'(your-package :location "~/path/to/your-package/")
With the variable dotspacemacs-additional-packages
, it is possible to
install extra packages which are not already included in any layers.
Dependencies should be explicitly included as they won’t be resolved
automatically. Here is a table of all the extra packages I use:
name of the package | why is it installed |
---|---|
caddyfile-mode | Major mode for editing Caddyfiles |
dired-du | alternative to ncdu with Dired |
doom-themes | some cool themes |
edit-indirect | edit region in separate buffer |
elcord | rich integration of Emacs in Discord |
kaolin-themes | some cool themes |
lsp-dart | apparently, it isn’t included in the Dart layer |
magit-gitflow | integrate gitflow in Magit |
multiple-cursors | I don’t like the layer, I prefer this package alone |
org-sidebar | display on the side the outline of an Org buffer |
org-tree-slide | presentation tool for org-mode |
outorg | edit comments as Org-mode buffers |
pinentry | enter a GPG password from Emacs |
s | The long lost Emacs string manipulation library. |
visual-fill-column | allow the use of fill-column in visual-line-mode |
wrap-region | easily wrap region with delimiters |
wttrin | weather in Emacs |
yasnippet-snippets | snippets for YaSnippet |
It is possible to also list packages that cannot be updated:
(setq-default dotspacemacs-frozen-packages '(helm-icons))
And to list packages which won’t be installed nor loaded:
(setq-default dotspacemacs-excluded-packages '(company-tern))
Finally, it is possible to define the behaviour of Spacemacs when installing packages. Possible values are:
-
used-only
- installs only explicitly used packages and deletes any unused packages as well as their unused dependencies
-
used-but-keep-unused
- installs only the used packages but won’t delete unused ones
-
all
- installs all packages supported by Spacemacs and never uninstalls them.
The default value is used-only
.
(setq-default dotspacemacs-install-packages 'used-only)
Layers
All layers are set one variable: dotspacemacs-configuration-layers
. This
variable is a list of layers, some of them will have some custom variables.
Typically, the variable will be set like so:
(setq-default dotspacemacs-configuration-layers
'(emacs-lisp
helm
multiple-cursors
org
(shell :variables shell-default-height
30 shell-default-position 'bottom)
treemacs))
Checkers
The two first checkers I use are for spell and syntax checking.
spell-checking
is disabled by default, however it should auto-detect the
dictionary to use.
(spell-checking :variables
spell-checking-enable-by-default nil
spell-checking-enable-auto-dictionary t)
syntax-checking
Completion
auto-completion
is a layer enabled in order to provide auto-completion to
all supported language layers. It is set so that the RET
key has no
behavior with this layer, however the TAB
key cycles between candidates
displayed by the auto-completion toolbox. I also want the autocompletion to
include snippets in the popup, and the content of the popup is sorted by
usage. It is also disabled for two modes: magit and Org.
(auto-completion :variables
auto-completion-complete-with-key-sequence-delay 0.2
auto-completion-enable-help-tooltip 'manual
auto-completion-enable-sort-by-usage t
:disabled-for
org
git)
helm
is also enabled, with its header disabled.
(helm :variables helm-no-header t)
I use as my daily Email client mu4e
, so let’s enable it and tell Emacs
where mu4e is installed. I also tell mu4e to use maildirs extensions, use
async operations, where to keep attachments, and enable the mu4e modeline.
(mu4e :variables
mu4e-installation-path "/usr/share/emacs/site-lisp"
mu4e-use-maildirs-extension t
mu4e-enable-mode-line t
mu4e-attachment-dir "~/Documents/mu4e")
Emacs
The first layer enabled in this category is better-defaults
. I also made
it so that when a command equivalent to C-a
or C-e
is pressed, the
cursor will respectively first move to the beginning of code first before
going past the indentation and to the end of the code before going to the
end of the line before going over the end of the comments on the same line.
(better-defaults :variables
better-defaults-move-to-beginning-of-code-first t
better-defaults-move-to-end-of-code-first t)
I also enabled ibuffer
and made it so that buffers are sorted by projects.
(ibuffer :variables
ibuffer-group-buffers-by 'projects)
Most important of all, the org
layer is also enabled. I enabled support
for Epub exports, Github, Reveal.JS exports, and sticky headers. Project
support is also enabled through files named TODOs.org
. I also set the
org-download folder for images in ~/Pictures/org/
, and I set the RET
key
to follow org links if the cursor is on one.
(org :variables
org-enable-epub-support t
org-enable-github-support t
org-enable-hugo-support t
org-enable-roam-support t
org-enable-reveal-js-support t
org-enable-sticky-header t
spaceline-org-clock-p t
org-projectile-file "TODOs.org"
org-download-image-dir "~/Pictures/org/"
org-return-follows-link t)
The semantic
layer is also enabled.
semantic
File trees
In this category, I only enabled one layer: treemacs
. In this layer, I set
is so that treemacs syncs with my current buffer, and it automatically
refreshes its buffer when there is a change in the part of the file system
shown by treemacs.
(treemacs :variables
treemacs-use-follow-mode t
treemacs-use-filewatch-mode t)
Fonts
In this category, again, one layer is enabled: unicode-fonts
. This layer
addssupport for the unicode-fonts
package.
(unicode-fonts :variables
unicode-fonts-enable-ligatures t
unicode-fonts-ligature-modes '(prog-mode)
unicode-fonts-ligature-set '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
"\\" "://"))
Fun
In this category, I only enabled two layers: selectric
and xkcd
.
selectric xkcd
Internationalization
In this category, I enabled the keyboard-layout
layer to enable
compatibility with the bépo layout. This layer, however, is disabled for
magit, Dired and eww.
(keyboard-layout :variables
kl-layout 'bepo
kl-disabled-configurations '(magit dired eww))
Programming languages
Domain-specific (DSLs)
In this category, I enabled support for the major-modes
layer for the
Arch Linux PKGBUILDs support, emacs-lisp
and scheme
layers, support for
the CSV format with the csv
layer, the yaml
language, shell scripting
languages and support for the dot
tool with the graphviz
layer.
major-modes emacs-lisp scheme graphviz yaml shell-scripts
I also added support for HTML and CSS with the html
layer, with the web
formatting tool set to web-beautify
, and the LSP layer compatibility
enabled for CSS, less, SCSS and HTML.
(html :variables
web-fmt-tool 'web-beautify
css-enable-lsp t
less-enable-lsp t
scss-enable-lsp t
html-enable-lsp t)
The json
layer is also enabled, with the format tool set to
web-beautify
.
(json :variables
json-fmt-tool 'web-beautify)
The LaTeX layer has also been enabled, with its default compiler set to XeLaTeX. I also enabled the auto-fill feature, the folding capacity and the “magic” symbols.
(latex :variables
latex-build-command "xelatex"
latex-enable-auto-fill t
latex-enable-folding t
latex-enable-magic t)
The Markdown layer has been enabled, with support for live preview with
vmd
, and and automatic MMM-mode generation for C, C++, Python, Rust and
Emacs Lisp.
(markdown :variables
markdown-live-preview-engine 'vmd
markdown-mmm-auto-modes '("c"
"c++"
"python"
"rust"
("elisp" "emacs-lisp")))
(bibtex :variables
org-ref-default-bibliography '("~/Documents/Papers/references.bib")
org-ref-pdf-directory "~/Documents/Papers"
org-ref-bibliography-notes "~/Documents/Papers/notes.org")
csv
Frameworks
Only one framework support has been enabled so far, and is is for the Django framework.
django
General-purpose
Among the layers I activated, the only one without any specific
configuration is the asm
layer for the Assembly language.
asm
Next, you can find the C/C++ layer for which I set the default language for
.h
files to be C. I also enabled support for subprojects and organization
of the include directives on a file save. I also set a couple of
LSP-related variables, such as the LSP executable for C/C++ for its CCLS
backend and some highlight variables.
(c-c++ :variables
c-c++-default-mode-for-headers 'c-mode
c-c++-adopt-subprojects t
c-c++-enable-c++11 t
c-c++-backend 'lsp-clangd
c-c++-lsp-enable-semantic-highlight t
c-c++-lsp-semantic-highlight-method 'overlay
c-c++-lsp-semantic-highlight-rainbow t
c++-enable-organize-includes-on-save t)
Dart has also been enabled, with a custom path to the SDK of the Dart server, and to the LSP server of Dart.
(dart :variables
lsp-dart-project-sdk-dir "/opt/dart-sdk/"
lsp-dart-sdk-dir "/opt/dart-sdk/")
When it comes to the Python layer, I set its backend and formatter to be bound to the LSP layer. Its fill columnn was also set to 80 characters, imports are sorted on save, and the tests can be run using either nose.el or pytest.
(python :variables
python-fill-column 80
python-test-runner '(pytest nose))
With the Rust layer, the only custom configuration set is the backend being bound to the LSP layer.
(rust :variables rust-backend 'lsp)
As regards the JavaScript layer, I set its backend to the LSP layer, and
bound its format tool to web-beautify
and its REPL is browser-based. I
also want to include node_modules/.bin
to be automatically added to the
buffer local exec_path
.
(javascript :variables
javascript-backend 'lsp
javascript-fmt-tool 'web-beautify
javascript-repl 'skewer
node-add-modules-path t)
Alternatively, I also use Typescript which is a sort of better Javascript as it should have been, with the LSP backend.
(typescript :variables
typescript-backend 'lsp)
I am also currently using the Awesome window manager which requires the Lua programming language, so here it is.
(lua :variables
lua-backend 'lsp-emmy
lua-lsp-emmy-jar-path "~/.config/awesome/EmmyLua-LS-all.jar"
lua-lsp-emmy-java-path "java"
lua-lsp-emmy-enable-file-watchers t)
Unfortunately, I have to write Swift code for one of my university courses, so here is the layer:
swift
Readers
Epub and Pdf readers
In this category, only the epub
and pdf
layers are enabled without any
special configuration, so I can read these files from Emacs directly.
epub pdf
Elfeed
Elfeed is an Emacs feeed and RSS reader which can be managed through org
files. Actually, through only one file in my case, located in my ~/org
directory.
(elfeed :variables
rmh-elfeed-org-files '("~/org/elfeed.org"))
Version control
Only the git
layer is enabled in this category.
git
Themes
Here, the colors
layer is the only one enabled. It activates support for
identifiers colorization, and strings representing colors.
colors
Tools
In this category, the first layer to be enabled is the CMake layer for which
I enabled support for the cmake-ide
package.
(cmake :variables
cmake-enable-cmake-ide-support t)
Next, we have the Docker, Nginx, Pass (the standard Unix password manager), Prettier, Systemd, Meson, Imenu-list, Web-beautify, Dap, Helpful, and LSP layers enabled.
dap docker helpful imenu-list lsp meson nginx pass prettier systemd
web-beautify
We also have the RestClient layer enabled for which I enabled the Org compatibility support.
(restclient :variables
restclient-use-org t)
LanguageTool works with Flyspell and will check for grammatical issues in my english texts.
(languagetool :variables
langtool-default-language "en-US"
languagetool-show-error-on-jump t
langtool-java-classpath "/usr/share/languagetool:/usr/share/java/languagetool/*")
And finally, we also have the Shell layer for which I specified its default height when spawning at the bottom of the screen should be 40 lines high, and the default shell to invoke is Eshell.
(shell :variables
shell-default-height 40
shell-default-position 'bottom
shell-default-shell 'eshell)
Web Services
In this category, I have only enabled a layer for Twitter support.
twitter
Custom layers
Lastly, three custom layers have been enabled: a w3m layer, and two of my custom layers for Dired and for conlanging tools.
conlanging dired-phundrak w3m
Init
The dotspacemacs/init
function is the one called at the very begining of the
Spacemacs startup, before any kind of configuration, including the layer
configuration. Only the values of the Spacemacs settings should be modified
here. By default, every values are set in a setq-default
sexp, and they
represent all the supported Spacemacs settings. Hence, the function looks like
this:
(defun dotspacemacs/init ()
(setq-default
;; default Spacemacs configuration here
))
Emacs with pdumper
It is possible to compile Emacs 27 from source with support for the portable
dumper, as shown in Spacemacs’ EXPERIMENTAL.org
file. I do not use this
feature yet, as I am still on Emacs 26 provided from Arch Linux’s
repositories, so I’ll disable the Spacemacs support for this feature. The
default value of this variable is nil
.
(setq-default dotspacemacs-enable-emacs-pdumper t)
In case the support for pdumper was enabled, Spacemacs needs to know the name
of the Emacs executable which supports such a feature. The executable must be
in the user’s PATH
. By default, the value of the variable is "emacs"
.
(setq-default dotspacemacs-emacs-pdumper-executable-file "emacs")
And finally, we can name the Spacemacs dump file. This is the file that will
be created by the portable dumper in the cache directory under the dumps
sub-directory. To load it when starting Emacs, the parameter --dump-file
should be added when invoking Emacs 27.1 executable from the command line,
for instance:
./emacs --dump-file=~/.config/emacs/.cache/dumps/spacemacs.pdmp
The default value of this variable is "spacemacs.pdmp"
.
(setq-default dotspacemacs-emacs-dumper-dump-file
(format "spacemacs-%s.pdmp" emacs-version))
Package managment and updates
Spacemacs’ core configuration can be updated via git commands using Github
services. If Spacemacs is not set to the develop
branch, it can check by
itself if any update is available. However, I am using said branch, therefore
I should set this variable to nil
. The default value is nil
.
(setq-default dotspacemacs-check-for-update nil)
When it comes to package management, Spacemacs is able to store them in
different directories depending on the version of Emacs used or based on
other variables. I personally prefer to use the value emacs-version
since
it makes it easier to upgrade or downgrade Emacs without any conflict with
the already installed packages. The default value is emacs-version
.
(setq-default dotspacemacs-elpa-subdirectory 'emacs-version)
Spacemacs has a capacity of performing rollbacks after updates. We can set
the maximum number of rollback slots to keep in the cache. The default value
is 5
.
(setq-default dotspacemacs-max-rollback-slots 5)
Elpa repository
It is possible to ask Emacs to use an HTTPS connection when contacting the
Elpa whenever possible. This value should be set to nil
when the user has
no way to contact the Elpa though HTTPS, otherwise it is strongly
recommended to let it set to t
. This variable however has no effect if
Emacs is launched with the parameter --insecure
which forces the value of
this variable to nil
. The default value is t
.
(setq-default dotspacemacs-elpa-https t)
We can set a maximum amount of seconds which will represent the maximum
allowed time to contact the Elpa repository. By default, this setting is set
on 5
.
(setq-default dotspacemacs-elpa-timeout 5)
Spacelpa repository
The Spacelpa repository is a Spacemacs-specific package repository. It is
possible to use it as the primary source to install a locked version of a
package. If the below value is set to nil
, then Spacemacs will install the
latest version of packages from MELPA. I personally don’t use it, so I let
it set to nil
. The default value is nil
.
(setq-default dotspacemacs-use-spacelpa nil)
If the below value is not nil
, then the signature for the downloaded
Spacelpa packages must be verified.
(setq-default dotspacemacs-verify-spacelpa-archives t)
Editing style
By default, Spacemacs encourages the use of evil-mode, which brings vim keybinding in Emacs. Still, it has three different styles available:
vim
, which goes full evil-mode usage and most adapted to Emacs newcomers, especially if they were used to vim before, with the use of a normal mode and an insert mode.emacs
which keeps an Emacs-like feel to the keybindings, without any difference between an insert or normal mode.hybrid
is a modification of thevim
mode which brings theemacs
style in insert mode, but otherwise behaves like thevim
style in normal mode. This is the style I personally use.
The value can also be a list with the :variables
keyword (similar to
layers). Check the editing styles section of the documentation for details on
available variables. The default value is vim
.
(setq-default dotspacemacs-editing-style
'(hybrid :variables
hybrid-mode-enable-evilified-state t
hybrid-mode-default-state 'normal))
If non-nil, the paste transient-state is enabled. While enabled, after you
paste something, pressing C-j
and C-k
several times cycles through the
elements in the kill-ring
. Default nil
.
(setq-default dotspacemacs-enable-paste-transient-state t)
Spacemacs home configuration
The value below specifies the startup banner of Spacemacs. The default value
is official
, it displays the official Spacemacs logo. An integer value is
the index of text banner, random
chooses a random text banner in the
core/banners
directory. A string value must be a path to an image format
supported by your Emacs build. If the value is nil, then no banner is
displayed. The default value is official
.
(setq-default dotspacemacs-startup-banner 'official)
On the Spacemacs homepage, a list of elements can also be shown, be it recent
files, projects, agenda items,… Each of the elements making up the list value
of the below variable are pairs in the form (list-type . list-size)
. If the
value is nil
, then it is disabled. The possible values for list-type
are:
-
recents
- displays recently opened files
-
bookmarks
- displays saved bookmarks
-
projects
- displays projectile projects recently opened
-
agenda
- displays upcoming events from Org-mode agendas
-
todos
- displays recent TODOs detected in projectile projects
The order in which they are set in the below list affects their order on the
Spacemacs startup page. List sikes may be nil
, in which case
spacemacs-buffer-startup-lists-length
takes effect.
(setq-default dotspacemacs-startup-lists '((recents . 15)
(projects . 15)))
The below variable allows the startup page to respond to resize events. Its
default value is t
.
(setq-default dotspacemacs-startup-buffer-responsive t)
If non-nil show the version string in the Spacemacs buffer. It will appear as
(spacemacs version)@(emacs version)
. Default t
.
(setq-default dotspacemacs-startup-buffer-show-version t)
Default major modes
The below variable sets a default major mode for a new empty buffer. Possible
values are mode names such as text-mode
, or nil
to use Fundamental mode.
The default value is text-mode
, but I prefer to use org-mode
by default.
(setq-default dotspacemacs-new-empty-buffer-major-mode 'org-mode)
Similarly, the below variable sets the default mode for the scratch buffer.
Its default value is text-mode
, but I also set it to use org-mode
by
default.
(setq-default dotspacemacs-scratch-mode 'org-mode)
By the way, it is possible to set a default message for the scratch buffer,
such as “Welcome to Spacemacs!”. I prefer to keep it clean. The default value
is nil
.
(setq-default dotspacemacs-initial-scratch-message nil)
Visual configuration
Themes
Spacemacs makes it quite easy to use themes and organize them. The below
value is a list of themes, the first of the list is loaded when Spacemacs
starts. The user can press SPC T n
to cycle to the next theme in the list.
(setq-default dotspacemacs-themes '(doom-nord doom-vibrant spacemacs-dark doom-one
doom-opera doom-dracula doom-molokai doom-peacock
doom-sourcerer doom-spacegrey kaolin-dark
kaolin-aurora kaolin-bubblegum kaolin-galaxy
kaolin-mono-dark kaolin-temple kaolin-valley-dark))
Emacs also makes use of themes for the Spaceline at the bottom of buffers. Supported themes are:
spacemacs
all-the-icons
custom
doom
(the one I use)vim-powerline
vanilla
The first three are Spaceline themes. doom
is the Doom-Emacs mode-line,
and vanilla
is the default Emacs mode-line. custom
is a user defined
theme, refer to Spacemacs’ DOCUMENTATION.org
file for more info on how to
create your own Spaceline theme. Value can be a symbol or list with
additional properties. The default value is '(spacemacs :separator wave
:separator-scale 1.5))
.
(setq-default dotspacemacs-mode-line-theme '(doom
:separator wave
:separator-scale 1.0))
It is also possible to color the cursor depending on which mode Spacemacs is
in, in order to mach the state color in GUI Emacs. The default value is t
.
(setq-default dotspacemacs-colorize-cursor-according-to-state t)
The below variable sets either the default font or a prioritized list of fonts to be used by Emacs.
(setq-default dotspacemacs-default-font '("Cascadia Code"
:size 9.0))
I also added the following code in order to define a fallback font for emojis, defined only on their unicode range:
(set-fontset-font "fontset-default" '(#x1f600 . #x1f64f) "NotoEmoji Nerd Font")
Other on-screen elements
which-key
is a helper which displays available keyboard shortcuts. This
variable sets in seconds the time Spacemacs should wait between a key press
and the moment which-key
should be shown.
(setq-default dotspacemacs-which-key-delay 1)
This variable sets which-key
's frame position. Possible values are:
right
bottom
right-then-bottom
right-then-bottom
tries to display the frame to the right, but if there is
insufficient space it displays it at the bottom. The default value is
bottom
.
(setq-default dotspacemacs-which-key-position 'right-then-bottom)
This controls where switch-to-buffer
displays the buffer. If the value is
nil
, switch-to-buffer
displays the buffer in the current window even if
another same-purpose window is available. If non-nil, switch-to-buffer
displays the buffer in a same-purpose window even if the buffer can be
displayed in the current window. The default value is nil
.
(setq-default dotspacemacs-switch-to-buffer-prefers-purpose nil)
If this variable is non-nil, a progress bar is displayed when Spacemacs is
loading. This may increase the boot time on some systems and emacs builds,
set it to nil
to boost the loading time. The default value is t
.
(setq-default dotspacemacs-loading-progress-bar t)
If the value is non-nil, Emacs will show the title of the transient states.
The default value is t
.
(setq-default dotspacemacs-show-transient-state-title t)
If non-nil, this will show the color guide hint for transient state keys.
The default value is t
.
(setq-default dotspacemacs-show-transient-state-color-guide t)
If non-nil, unicode symbols are displayed in the mode line. If you use Emacs
as a daemon and want unicode characters only in GUI set the value to quoted
display-graphic-p
. The default value is t
.
(setq-default dotspacemacs-mode-line-unicode-symbols t)
If non-nil, smooth scrolling (native-scrolling) is enabled. Smooth scrolling
overrides the default behavior of Emacs which recenters point when it
reaches the top or bottom of the screen. The default value is t
.
(setq-default dotspacemacs-smooth-scrolling t)
The following value controls the line number activation. If set to t
,
relative
or visual
then line numbers are enabled in all prog-mode
and
text-mode
derivatives. If set to relative
, line numbers are relative. If
set to visual
, line numbers are also relative, but only visual lines are
counted. For example, folded lines will not be counted and wrapped lines are
counted as multiple lines. This variable can also be set to a property list
for finer control:
'(:relative nil
:visual nil
:disabled-for-modes dired-mode
doc-view-mode
markdown-mode
org-mode
pdf-view-mode
text-mode
:size-limit-kb 1000)
When used in a plist, visual
takes precendence over relative
.
(setq-default dotspacemacs-line-numbers '(:relative nil
:enabled-for-modes prog-mode))
Select a scope to highlight delimiter. Possible values are:
any
current
all
nil
The default value is all
(highlights any scope and emphasis the current
one).
(setq-default dotspacemacs-highlight-delimiters 'all)
After a certain amount of time in seconds, Spacemacs can zone-out. The
default value is nil
. I set it so Spacemacs zones out after 15 minutes.
(setq-default dotspacemacs-zone-out-when-idle 900)
Run spacemacs/prettify-org-buffer
when visiting the README.org
files of
Spacemacs. The default value is nil
.
(setq-default dotspacemacs-pretty-docs nil)
If nil
, the home buffer shows the full path of agenda items and todos. If
non nil
, only the file name is shown.
(setq-default dotspacemacs-home-shorten-agenda-source t)
Appearance of Emacs frames
Starting from Emacs 24.4, it is possible to make the Emacs frame fullscreen
when Emacs starts up if the variable is set to a non-nil value. The default
value is nil
.
(setq-default dotspacemacs-fullscreen-at-startup nil)
This variable is to be used if the user does not want to use native
fullscreen with spacemacs/toggle-fullscreen
. This disables for instance
the fullscreen animation under OSX. The default value is nil
.
(setq-default dotspacemacs-fullscreen-use-non-native nil)
If you do not start Emacs in fullscreen at startup, you might want it to be
maximized by default. If the value for the variable below is set to be
non-nil, the frame will be maximized. This can only work if
dotspacemacs-fullscreen-at-startup
is set to nil
, and it is only
available from Emacs 24.4 onwards. The default value is nil
.
(setq-default dotspacemacs-maximized-at-startup nil)
If non-nil, the frame is undecorated when Emacs starts up. Combine this with
the variable dotspacemacs-maximized-at-startup
in OSX to obtain borderless
fullscreen. The default value is nil
.
(setq-default dotspacemacs-undecorated-at-startup nil)
You can also set a transparency level for Emacs when you toggle the
transparency of the frame with toggle-transparency
. The value of the
transparency, going from 0 to 100 in increasing opacity, describes the
transparency level of a frame when it’s active or selected. The default
value is 90
.
(setq-default dotspacemacs-active-transparency 75)
Similarly, you can set a value from 0 to 100 in increasing opacity which
describes the transparency level of a frame when it’s inactive or
deselected. The default value is 90
.
(setq-default dotspacemacs-inactive-transparency 80)
The variable below sets the format of frame title. You can use:
-
%a
- the
abbreviated-file-name
orbuffer-name
-
%t
projectile-project-name
-
%I
invocation-name
-
%S
system-name
-
%U
- contents of
$USER
-
%b
- buffer name
-
%f
- visited file name
-
%F
- frame name
-
%s
- process status
-
%p
- percent of buffer above top of window, or Top, Bot, or All
-
%P
- percent of buffer above bottom of window, perhaps plus Top, or Bot, or All
-
%m
- mode name
-
%n
- Narrow if appropriate
-
%z
- mnemonics of buffer, terminal, and keyboard coding systems
-
%Z
- like
%z
, but including the end-of-line format
The default value is "%I@%S"
.
(setq-default dotspacemacs-frame-title-format "Emacs: %b (%t) %U@%S")
Format specification for setting the icon title format. The default value is
nil
, same as frame-title-format
.
(setq-default dotspacemacs-icon-title-format nil)
Spacemacs leader keys and shortcuts
The below setting sets the Spacemacs leader key. By default, this is the
SPC
key.
(setq-default dotspacemacs-leader-key "SPC")
Once the leader key has been pressed, it is possible to set another key in
order to call Emacs’ command M-x
. By default, it is again the SPC
key.
(setq-default dotspacemacs-emacs-command-key "SPC")
It is also possible to invoke Vim Ex commands with the press of a key, and by
default it is the :
key.
(setq-default dotspacemacs-ex-command-key ":")
The below variable sets the leader key accessible in emacs-state
and
insert-state
:
(setq-default dotspacemacs-emacs-leader-key "M-m")
The major mode leader key is a shortcut key which is the equivalent of
pressing <leader> m
. Set it to nil
to disable it. Its default value is
,
.
(setq-default dotspacemacs-major-mode-leader-key ",")
In emacs-state
and insert-state
, the same major mode leader key can be
accessible from another shortcut, which by default is C-M-m
in terminal
mode, or M-return
in GUI mode.
(setq-default dotspacemacs-major-mode-emacs-leader-key
(if window-system "<M-return>" "C-M-m"))
These variables control whether separate commands are bound in the GUI to the
key pairs C-i
and TAB
, and C-m
and RET
. Setting it to a non-nil value
allows for separate commands under C-i
and TAB
, and C-m
and RET
. In
the terminal, these pairs are generally indistinguishable, so this only works
in the GUI. The default value is nil
.
(setq-default dotspacemacs-distinguish-gui-tab nil)
Layouts
The variable belows sets the name of the default layout. Its default value is
"Default"
.
(setq-default dotspacemacs-default-layout-name "Default")
If non-nil, the default layout name is displayed in the mode-line. The
default value is nil
.
(setq-default dotspacemacs-display-default-layout nil)
If non-nil, then the last auto saved layouts are resumed automatically upon
start. The default value is nil
.
(setq-default dotspacemacs-auto-resume-layouts nil)
If non-nil, the layout name will be auto-generated when creating new layouts.
It only has an effect when using the “jump to layout by number” command. The
default value is nil
.
(setq-default dotspacemacs-auto-generate-layout-names nil)
Emacs server
Emacs can be launched as a server if the following value is set to non-nil
and if one isn’t already running. The default value is nil
.
(setq-default dotspacemacs-enable-server nil)
You can also set a custom emacs server socket location. If the value is
nil
, Emacs will use whatever the Emacs default is, otherwise a directory
path like "$HOME/.config/emacs/server"
. It has no effect if
dotspacemacs-enable-server
is nil
.
(setq-default dotspacemacs-server-socket-dir nil)
It is also possible to tell Emacs that the quit function should keep the
server open when quitting. The default value is nil
.
(setq-default dotspacemacs-persistent-server t)
Miscellaneous
This value changes the folding method of code blocks. The possible values are
either evil
, the default value, or origami
.
(setq-default dotspacemacs-folding-method 'evil)
If non-nil, smartparens-strict-mode
will be enabled in programming modes.
The default value is nil
.
(setq-default dotspacemacs-smartparens-strict-mode nil)
If non-nil, pressing the closing parenthesis )
key in insert mode passes
over any automatically added closing parenthesis, bracket, quote, etc… This
can temporarily disabled by pressing C-q
before )
. The default value is
nil
.
(setq-default dotspacemacs-smart-closing-parenthesis nil)
List of search tool executable names. Spacemacs uses the first installed tool of the list. Supported tools are:
rg
ag
pt
ack
grep
The default value is '("rg" "ag" "pt" "ack" "grep")
.
(setq-default dotspacemacs-search-tools '("rg" "grep"))
Delete whitespace while saving buffer. Possible values are:
-
all
- aggresively delete empty lines and long sequences of whitespace
-
trailing
- only detele the whitespace at end of lines
-
changed
- to delete only whitespace for changed lines
-
nil
- disable cleanup
The default value is nil
.
(setq-default dotspacemacs-whitespace-cleanup nil)
Set gc-cons-threshold
and gc-cons-percentage
when startup finishes. This
is an advanced option and should not be changed unless you suspect performance
issues due to garbage collection operations. The default is '(100000000 0.1)
(setq-default dotspacemacs-gc-cons '(100000000 0.1))
If non nil activate clean-aindent-mode
which tries to correct virtual
indentation of simple modes. This can interfer with mode specific indent
handling like has been reported for go-mode
. If it does deactivate it here.
Default t
.
(setq-default dotspacemacs-use-clean-aindent-mode t)
If non nil
, shift your number row to match the entered keyboard layout (only
in insert state). Currently supported keyboard layouts are querty-us
,
quertz-de
and querty-ca-fr
. New layouts can be added in
spacemacs-editing
layer. Default nil
.
(setq-default dotspacemacs-swap-number-row nil)
Set read-process-output-max
when startup finishes. This defines how much
data is read from a foreign process. Setting this >= 1 MB should increase
performance for lsp servers in emacs 27.
(setq-default dotspacemacs-read-process-output-max (* 1024 1024 8))
User Initialization
While Emacs and especially Spacemacs loads, I want it to initialize some elements and load some packages. First of all, I want it to load my private Emacs config file:
(load "~/.config/emacs/private/private_emacs")
Then, I want a couple of requires:
(require 'org-id)
(require 'org-protocol)
(require 'package)
(require 'ox-latex)
(require 'ox-publish)
(require 'tramp)
I would also like to enable the setup of flycheck for Rust when Flycheck is loaded:
(add-hook 'flycheck-mode-hook #'flycheck-rust-setup)
By default, Flyspell should be disabled and only enabled manually.
(flyspell-mode 0)
Finally, here is a quick workaround for Tramp, sometimes it cannot connect to my hosts if I don’t have this code snippet.
(setq tramp-ssh-controlmaster-options
"-o ControlMaster=auto -o ControlPath='tramp.%%C' -o ControlPersist=no")
User Configuration
Custom functions
In this section, I will put my various custom functions that do not fit in other sections and which are more oriented towards general usage throughout Emacs and in Elisp code.
Almost all of my code snippets will be prefixed by either my name or the name of the package or layer they are part of, unless they are an explicit overwrite of a function that already exists.
phundrak/yas-rust-new-assignments
The following function is a function that will allow me to easily create
new
functions for Rust structs. Inspired from elpy’s
elpy-snippet-init-assignments
function, it will automatically write
assignments to my new struct as I write new parameters in the new
function. It also comes with a helper function that parses the arguments
given to the new
function.
(defun phundrak//yas-snippet-split-rust-args ($arg-string)
"Split a Rust argument string into ((name, default)...) tuples"
(mapcar (lambda ($elem)
(split-string $elem "[[:blank:]]*:[[:blank:]]*" t))
(split-string $arg-string "[[:blank:]]*,[[:blank:]]*" t)))
(defun phundrak/yas-rust-new-assignments ($arg-string)
"Return a typical new assignment for arguments.
Inspired from elpy’s functions https://github.com/jorgenschaefer/elpy"
(let ((indentation (make-string (save-excursion
(goto-char start-point)
(current-indentation))
?\s)))
(mapconcat (lambda ($elem)
(if (string-match "^\\*" (car $elem))
""
(format "%s,\n%s" (car $elem) indentation)))
(phundrak//yas-snippet-split-rust-args $arg-string)
"")))
phundrak/add-all-to-list
(defun phundrak/add-all-to-list ($list &rest $elements)
"Add all $elements from `$elements' to the $list `$list'. If an
element from `$elements' is already part of `$list', it will be
ignored."
(dolist ($e $elements)
(if (not (member $e $list))
(add-to-list '$list $e)))
$list)
phundrak/fill-paragraph
This function was created in order to bind to another keyboard shortcut the
already existing C-u M-q
which I cannot type with evil-mode unless I’m in
insert mode.
(defun phundrak/fill-paragraph ()
(interactive)
(let* ((current-prefix-arg 4))
(call-interactively 'fill-paragraph)))
terminal-here-default-terminal-command
This function is actually an overwrite of the default one which apparently
does not work on my machine. This function is called by
terminal-here-launch
and spawns an external terminal emulator in the
directory emacs was in when the terminal was invoked. I simply point out to
this function the name of my terminal emulator. Here is the code:
(defun terminal-here-default-terminal-command (_dir)
'("st"))
phundrak/find-org-file
There are lots of files which I want to be able to quickly open. I used to
have one shortcut for each one of these files, but as their number grew, I
decided to switch to helm for my file selector which will be called by only
one common shortcut. Most of my files will be located in ~/org
, but I have
some conlanging files which are located in ~/Documents/conlanging
, and all
my university notes are in ~/Documents/university
. Let’s declare these
directories in a variable:
(setq phundrak/org-directories '("~/org"
"~/Documents/university"
"~/Documents/conlanging"))
With this established, let’s write some emacs-lisp that will allow me to get a list of all these files and select them through helm. Be aware that I will be using some functions from third party packages, such as f.el and dash.
(defun phundrak/find-org-file ()
(interactive)
(find-file
(helm
:sources (helm-build-sync-source "org files"
:candidates (-mapcat
(lambda (path)
(f-files path
(lambda (file) (equal (f-ext file) "org"))
t))
phundrak/org-directories)
:fuzzy-match t)
:buffer "*org files*")))
Emacs builtins
Dired
When it comes to dired, I chose do modify some elements on how things are sorted and shown, but there isn’t much configuration. First, I want to always copy folders in a recursive way, no questions asked.
(setq dired-recursive-copies 'always)
Also, when I have two Dired buffers opened side by side, I generally want them to interact, for example if I want to move something around. So, let’s tell Emacs that:
(setq dired-dwim-target t)
Finally, let’s tell Dired how to sort the elements to be displayed: directories first, non-hidden first.
(setq dired-listing-switches "-ahl --group-directories-first")
By the way, let’s enable org-download
when we are in a Dired buffer:
(add-hook 'dired-mode-hook 'org-download-enable)
Eshell
Eshell is a built-in shell available from Emacs which I use almost as often as Fish. Some adjustments are necessary for making this shell usable for me.
Environment variables
Some environment variables need to be correctly set so Eshell can correctly
work. The first environment variable to be set is the PATH
, as I have a
couple of directories where executables are located. Let’s add them to our
path.
(setenv "PATH"
(concat
(getenv "HOME") "/.pub-cache/bin"
":" (getenv "HOME") "/.local/bin"
":" (getenv "HOME") "/go/bin"
":" (getenv "HOME") "/.cargo/bin"
":" (getenv "HOME") "/.gem/ruby/2.6.0/bin"
":" (getenv "PATH")))
I would also like to set two environment variables related to Dart
development: the DART_SDK
and ANDROID_HOME
variables.
(setenv "DART_SDK" "/opt/dart-sdk/bin")
(setenv "ANDROID_HOME" (concat (getenv "HOME") "/Android/Sdk/"))
Finally, I’d like to add a custom directory to the PKG_CONFIG_PATH
:
(setenv "PKG_CONFIG_PATH" (concat
"/usr/local/lib/pkgconfig/" ":"
(getenv "PKG_CONFIG_PATH")))
The EDITOR
variable also needs to be set for git commands, especially the
yadm
commands.
(setenv "EDITOR" "emacsclient -c")
Custom functions
When I’m in Eshell, sometimes I wish to open multiple files at once in
Emacs. For this, when I have several arguments for find-file
, I want to be
able to open them all at once. Let’s modify find-file
like so:
(defadvice find-file (around find-files activate)
"Also find all files within a list of files. This even works recursively."
(if (listp filename)
(loop for f in filename do (find-file f wildcards))
ad-do-it))
I also want to be able to have multiple instances of Eshell opened at once.
For that, I declared the function eshell-new
that does exactly that.
(defun eshell-new()
"Open a new instance of eshell."
(interactive)
(eshell 'N))
Redirect text editors to Emacs
I still have some muscle memory telling me to open nano, ed, or vim, and
sometimes I even try to type emacs
in the terminal, which is stupid with
Eshell since I’m already inside Emacs. So, for each of these text editors,
let’s make the command open the files in Emacs.
(defun eshell/emacs (&rest $files)
"Open a file in a new buffer. Old habits die hard"
(if (null $files)
(bury-buffer)
(mapc #'find-file
(mapcar #'expand-file-name
(eshell-flatten-list (reverse $files))))))
(defalias 'eshell/vi 'eshell/emacs)
(defalias 'eshell/vim 'eshell/emacs)
(defalias 'eshell/ed 'eshell/emacs)
(defalias 'eshell/nano 'eshell/emacs)
Aliases
This function is a function that will come in very handy for Eshell
functions that call shell processes. It concatenates the initial string
command
with all the arguments args
, each separated with a space.
(defun phundrak/concatenate-shell-command ($command &rest $args)
(string-join (cons $command $args) " "))
Just like most shells, it is possible to declare in Eshell aliases. First, I
would like to be able to use open
to open files in Emacs:
(defalias 'open 'find-file)
I also have openo
which allows me to perform the same action, but in
another window:
(defalias 'openo 'find-file-other-window)
The function yes-or-no-p
is also aliased to y-or-n-p
so I only have to
answer by y
or n
instead of typing yes
or no
.
(defalias 'yes-or-no-p 'y-or-n-p)
For some ease of use, I’ll also declare list-buffers
as an alias of
ibuffer
.
(defalias 'list-buffers 'ibuffer)
(defun eshell/mkdir ($directory)
(make-directory $directory t))
(defun eshell/ls (&optional $directory)
(shell-command (format "exa -halg@ --group-directories-first --git%s"
(if (string= "" $directory)
""
(concat " " $directory)))))
System monitoring
Similar to meminfo
, we also have gpumeminfo
so we can get a quick look
at the memory-related logs of our X session.
(defun eshell/gpumeminfo (&rest $args)
(eshell/grep "-i" "--color" "memory" "/var/log/Xorg.0.log"))
(defun eshell/diskspace ()
(shell-command "sudo df -h | grep -E \"sd|lv|Size\" | sort"))
I also declared cpuinfo
an alias of lscpu
in order to keep consistent
with meminfo
.
(defun eshell/meminfo ()
(shell-command "free -m -l -t"))
pscpu
gives us information on what the CPU is running right now, and
I also declared cpuinfo
an alias of lscpu
in order to keep consistent
with meminfo
.
(defun eshell/cpuinfo ()
(shell-command "lscpu"))
pscpu10
limits that to the top 10 threads.
(defun eshell/pscpu ()
(shell-command "ps auxf | sort -nr -k 3"))
(defun eshell/pscpu10 ()
(shell-command "ps auxf | sort -nr -k 3 | head -10"))
Similarly, psmem
gives us information on the memory usage of the current
threads, and psmem10
only the ten most important threads in terms of
memory usage.
(defun eshell/pscpu ()
(shell-command "ps auxf | sort -nr -k 4"))
(defun eshell/pscpu10 ()
(shell-command "ps auxf | sort -nr -k 4 | head -10"))
System management (packages and services)
The first command is remove
which removes a package and its dependencies
from the system.
(defun eshell/remove (&rest $args)
(phundrak/concatenate-shell-command "sudo pacman -Rscnd"
$args))
But if I just want to run pacman
as sudo, then I could always just type
p
.
(defun eshell/p (&rest $args)
(phundrak/concatenate-shell-command "sudo pacman" $args))
Sometimes, I just want to purge my package manager’s cache, be it
pacman
's or yay
's. This is why I simply type purge
.
(defun eshell/purge ()
(shell-command "yay -Sc"))
Other
mkcd
is a function that allows me to create a directory and cd
into it
at the same time.
(defun eshell/mkcd ($directory)
(eshell/mkdir "-p" $directory)
(cd $directory))
(defun eshell/lsl (&rest $args)
(eshell/ls "-aHl" $args))
(defun eshell/ll (&rest $args)
(eshell/ls "-ahl" $args))
(defun eshell/la (&rest $args)
(eshell/ls "-A" $args))
Typos
q
is a shorthand for exit
. exti
and exi
are for typos when I type exit
.
(defun eshell/q (&rest $args)
(eshell/exit $args))
(defun eshell/exti (&rest $args)
(eshell/exit $args))
(defun eshell/exi (&rest $args)
(eshell/exit $args))
clean
is also a typo of clear
I often make. Let’s fix this:
(defun eshell/clean (&rest $args)
(eshell/clear $args))
Visual commands
With Eshell, some commands don’t work very well, especially commands that create a TUI. So, let’s declare them as visual commands or subcommands:
(setq eshell-visual-commands
'("fish" "zsh" "bash" "tmux" "htop" "top" "vim" "bat" "nano")
eshell-visual-subcommands
'("git" "log" "l" "diff" "show"))
Eshell theme
As with most shells, again, it is possible to customize the appearance of the Eshell prompt. First, we need to declare a macro so we can set a face with properties:
(defmacro with-face (str &rest properties)
`(propertize ,str 'face (list ,@properties)))
Now, let’s declare a function that will abbreviate the current pwd
fish-shell style.
(defun eshell/abbr-pwd ()
(let (($home (getenv "HOME"))
($path (eshell/pwd)))
(cond
((string-equal $home $path) "~")
((f-ancestor-of? $home $path) (concat "~/" (f-relative $path $home)))
($path))))
Now, let’s declare our prompt:
(defun eshell/my-prompt ()
(let ((header-bg "#161616"))
(concat
(with-face (eshell/abbr-pwd) :foreground "#008700")
"\n"
(if (= (user-uid) 0)
(with-face "➜" :foreground "red")
(with-face "➜" :foreground "#2345ba"))
" ")))
Now, let’s declare our prompt regexp and our prompt functions:
(setq eshell-prompt-regexp "^[^#$\n]*[#$] "
eshell-prompt-function 'eshell/my-prompt)
Org-mode
Org-mode is probably one of the best if not the best Emacs feature I have ever discovered. It is awesome for writing documents, regardless of the format you need it to be exported to, for agenda management, and for literary programming, such as with this document.
(with-eval-after-load 'org
;; configuration goes here
)
Custom org-mode functions
We begin with a couple of custom functions that I use in my org-mode files.
Custom and unique headings ID
The first ones are dedicated to provide org-mode headings a fixed and unique ID that won’t change over time. This code was taken from https://writequit.org/articles/emacs-org-mode-generate-ids.html. The first function’s job is to create these unique IDs
(defun eos/org-id-new (&optional prefix)
"Create a new globally unique ID.
An ID consists of two parts separated by a colon:
- a prefix
- a unique part that will be created according to
`org-id-method'.
PREFIX can specify the prefix, the default is given by the
variable `org-id-prefix'. However, if PREFIX is the symbol
`none', don't use any prefix even if `org-id-prefix' specifies
one.
So a typical ID could look like \"Org-4nd91V40HI\"."
(let* ((prefix (if (eq prefix 'none)
""
(concat (or prefix org-id-prefix)
"-"))) unique)
(if (equal prefix "-")
(setq prefix ""))
(cond
((memq org-id-method
'(uuidgen uuid))
(setq unique (org-trim (shell-command-to-string org-id-uuid-program)))
(unless (org-uuidgen-p unique)
(setq unique (org-id-uuid))))
((eq org-id-method 'org)
(let* ((etime (org-reverse-string (org-id-time-to-b36)))
(postfix (if org-id-include-domain
(progn
(require 'message)
(concat "@"
(message-make-fqdn))))))
(setq unique (concat etime postfix))))
(t (error "Invalid `org-id-method'")))
(concat prefix (car (split-string unique "-")))))
Now, let’s see the function that will be used to get the custom id of a heading at point. If the function does not detect any custom ID, then one should be created and inserted.
(defun eos/org-custom-id-get (&optional pom create prefix)
"Get the CUSTOM_ID property of the entry at point-or-marker POM.
If POM is nil, refer to the entry at point. If the entry does not
have an CUSTOM_ID, the function returns nil. However, when CREATE
is non nil, create a CUSTOM_ID if none is present already. PREFIX
will be passed through to `eos/org-id-new'. In any case, the
CUSTOM_ID of the entry is returned."
(interactive)
(org-with-point-at pom
(let* ((orgpath (mapconcat #'identity (org-get-outline-path) "-"))
(heading (replace-regexp-in-string
"/\\|~" ""
(replace-regexp-in-string
" " "_" (if (string= orgpath "")
(org-get-heading t t t t)
(concat orgpath "-" (org-get-heading t t t t))))))
(id (org-entry-get nil "CUSTOM_ID")))
(cond
((and id
(stringp id)
(string-match "\\S-" id)) id)
(create (setq id (eos/org-id-new (concat prefix heading)))
(org-entry-put pom "CUSTOM_ID" id)
(org-id-add-location id
(buffer-file-name (buffer-base-buffer)))
id)))))
Finally, this is the function that gets called on file saves. If the
function detects auto-id:t
among the org options in the #+OPTIONS:
header, then the above function is called.
(defun eos/org-add-ids-to-headlines-in-file ()
"Add CUSTOM_ID properties to all headlines in the current file
which do not already have one.
Only adds ids if the `auto-id' option is set to `t' in the file
somewhere. ie, #+OPTIONS: auto-id:t"
(interactive)
(save-excursion
(widen)
(goto-char (point-min))
(when (re-search-forward "^#\\+OPTIONS:.*auto-id:t"
(point-max)
t)
(org-map-entries (lambda ()
(eos/org-custom-id-get (point)
'create))))))
Let’s add a hook to the above function so it is called automatically on save, and only in read-write functions.
(add-hook 'org-mode-hook
(lambda ()
(add-hook 'before-save-hook
(lambda ()
(when (and (eq major-mode 'org-mode)
(eq buffer-read-only nil))
(eos/org-add-ids-to-headlines-in-file))))))
Org babel languages
One of the amazing features of org-mode is its literary programming capacities by running code blocks from within Org-mode itself. But for that, only a couple of languages are supported directly by Org-mode itself, and they need to be activated. Here are the languages I activated in my Org-mode configuration:
C |
dot |
emacs-lisp |
gnuplot |
latex |
makefile |
python |
sass |
scheme |
shell |
(C . t) (dot . t) (emacs-lisp . t) (gnuplot . t) (latex . t) (makefile . t) (python . t) (sass . t) (scheme . t) (shell . t)
The corresponding code is as follows:
(org-babel-do-load-languages
'org-babel-load-languages
'(
<<org-babel-languages-gen()>>))
Scheme requires a default implementation for geiser:
(setq geiser-default-implementation 'racket)
By the way, I wish to see source code behave the same way in the source blocks as in their own major mode. Let’s tell Emacs so:
(setq org-src-tab-acts-natively t)
Org variables
User information
Some variables about myself need to be set so Org-mode knows what information to include in exported files.
(setq user-full-name "Lucien Cartier-Tilet"
user-real-login-name "Lucien Cartier-Tilet"
user-login-name "phundrak"
user-mail-address "lucien@phundrak.com")
Visual settings
Visually, I prefer to hide the markers of macros, so let’s do that:
(setq org-hide-macro-markers t)
I also have an issue where small dots precede my org headers. Let’s fix that:
(setq org-hide-leading-stars nil
org-superstar-leading-bullet ?\s)
Org behavior
Here is one behavior that I really want to see modified: the ability to use
M-RET
without slicing the text the marker is on.
(setq org-M-RET-may-split-line nil)
I also have added a couple of custom structure templates for Org mode (>= 9.3), mainly for source code blocks.
(add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp"))
Since Org 9.3, Org no longer attempts to restore the window configuration
in the frame to which the user returns after editing a source block with
org-edit-src-code
. This means with the original value of
org-src-window-setup
(reorganize-frame
), the current frame will be
split in two between the original org window and the source window, and
once we quit the source window only the org window will remain. This is not
a desired behavior for me, so I chose to set this variable to
split-window-right
in order to keep my windows organization and have a
similar behavior to the old one.
;; (setq org-src-window-setup 'split-window-right)
(setq org-src-window-setup 'split-window-below)
However, it is not rare that I want to change that for an horizontal split,
which can be achieved with the value split-window-below
. Thus, I have
made this function that allows me to switch between the (default) vertical
split and the horizontal split.
(defun phundrak/toggle-org-src-window-split ()
"This function allows the user to toggle the behavior of
`org-edit-src-code'. If the variable `org-src-window-setup' has
the value `split-window-right', then it will be changed to
`split-window-below'. Otherwise, it will be set back to
`split-window-right'"
(interactive)
(if (equal org-src-window-setup 'split-window-right)
(setq org-src-window-setup 'split-window-below)
(setq org-src-window-setup 'split-window-right))
(message "Org-src buffers will now split %s"
(if (equal org-src-window-setup 'split-window-right)
"vertically"
"horizontally")))
Lastly, I know this can be a terrible idea, but I want Emacs to just evaluate Org code blocks without asking me. Of course, this could represent some big security issue if not careful enough, but I generaly just open my own org files.
(setq org-confirm-babel-evaluate nil)
Miscellaneous
When creating a link to an Org flie, I want to create an ID only if the link is created interactively, and only if there is no custom ID already created.
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
Org files exports
When it comes to exports, I want the LaTeX and PDF exports to be done with XeLaTeX only. This implies the modification of the following variable:
(setq org-latex-compiler "xelatex")
I also want to get by default minted
for LaTeX listings so I can have
syntax highlights:
(setq org-latex-listings 'minted)
The default packages break my LaTeX exports: for some reasons, images are
not loaded and exported in PDFs, so I needed to redifine the default
packages excluding the one that broke my exports. I also added two default
packages, minted
and xeCJK
for syntax highlighting and Japanese (and
additionally Chinese and Korean) support.
(setq org-latex-default-packages-alist '(("" "graphicx" t)
("T1" "fontspec" t ("pdflatex"))
("" "longtable" nil)
("" "wrapfig" nil)
("" "rotating" nil)
("normalem" "ulem" t)
("" "amsmath" t)
("" "textcomp" t)
("" "amssymb" t)
("" "capt-of" nil)
("" "minted" nil)
("" "xeCJK" nil)
("" "hyperref" nil)))
By the way, reference links in LaTeX should be written in this format:
(setq org-export-latex-hyperref-format "\\ref{%s}")
When it comes to the export itself, the latex file needs to be processed several times through XeLaTeX.
(setq org-latex-pdf-process
'("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
For Reveal.JS exports, I need to set where to find the framework by default:
(setq org-reveal-root "file:///home/phundrak/fromGIT/reveal.js")
I also want to disable by default behavior of ^
and _
for only one
character, making it compulsory to use instead ^{}
and _{}
respectively.
This is due to my frequent usage of the underscore in my org files as a
regular character and not a markup one. So, let’s disable it:
(setq org-use-sub-superscripts (quote {}))
On HTML exports, Org-mode tries to include a validation link for the exported HTML. Let’s disable that since I never use it.
(setq org-html-validation-link nil)
Custom LaTeX formats
I currently have two custom formats for my Org-mode exports: one for general
use (initialy for my conlanging files, hence its conlang
name), and one
for beamer exports.
Below is the declaration of the conlang
LaTeX class:
'("conlang"
"\\documentclass{book}"
("\\chapter{%s}" . "\\chapter*{%s}")
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
And here is the declaration of the beamer
class:
`("beamer"
,(concat "\\documentclass[presentation]{beamer}\n"
"[DEFAULT-PACKAGES]"
"[PACKAGES]"
"[EXTRA]\n")
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
Both these classes have to be added to org-latex-classes
like so:
(eval-after-load "ox-latex"
'(progn
(add-to-list 'org-latex-classes
<<org-latex-class-conlang>>
)
(add-to-list 'org-latex-classes
<<org-latex-class-beamer>>
)))
Org agenda
One awesome feature of Org mode is the agenda. By default, my agendas are
stored in ~/org/agenda
.
(setq org-agenda-files (list "~/org/agenda" "~/org/notes.org"))
I also have a custom command in Org agenda to mark some tasks as daily
tasks with the :DAILY:
tag,:
(setq org-agenda-custom-commands
'(("h" "Daily habits"
((agenda ""))
((org-agenda-show-log t)
(org-agenda-ndays 7)
(org-agenda-log-mode-items '(state))
(org-agenda-skip-function
'(org-agenda-skip-entry-if 'notregexp
":DAILY:"))))
("Y" "Yearly events"
((agenda ""))
((org-agenda-show-log t)
(org-agenda-ndays 365)
(org-agenda-log-mode-items '(state))
(org-agenda-skip-entry-if 'notregexp
":YEARLY:")))))
Org capture
Org-capture is an amazing feature of Org-mode which allows me to quickly save links, resources, reminders, and notes in neatly organized org files. Here they are described:
(setq
org-conlanging-file "~/org/conlanging.org"
org-default-notes-file "~/org/notes.org"
org-journal-file "~/org/journal.org"
org-linguistics-notes-file "~/org/linguistics-notes.org"
org-novel-notes-file "~/org/novel-notes.org"
org-private-agenda-file "~/org/agenda/private.org"
org-school-agenda-file "~/org/agenda/school.org"
org-wordbuilding-file "~/org/worldbuilding.org")
With Spacemacs, an Org capture can be invoked with the shortcut SPC a o c
.
It will then ask which template I wish to use. In the table below are
described the shortcuts that are available after SPC a o c
is invoked. The
name will be the one displayed in Org capture’s interface, the title is
the headline where to save the capture (if it does not differ from the
capture’s name, the cell will be blank). The insertion mode tells Emacs
how to add the capture to the file, using which template. A line with no
insertion mode, file, or template is just a category. All of the following
insert entries to their org files, that is a new org node with a headline
and some content.
Shortcut | Name | Title | Insertion mode | file | template |
---|---|---|---|---|---|
e | |||||
ew | Write Email | Emails | file+headline | org-default-notes-file | emails.orgcaptmpl |
j | Journal | file+datetree | org-journal-file | journal.orgcaptmpl | |
l | Link | ||||
ll | General | file+headline | org-default-notes-file | ||
ly | YouTube | file+headline | org-default-notes-file | youtube.orgcaptmpl | |
L | Protocol Link | Link | file+headline | org-default-notes-file | protocol-link.orgcaptmpl |
n | Notes | ||||
nc | Conlanging | Note | file+headline | org-conlanging-file | notes.orgcaptmpl |
nn | General | file+headline | org-default-notes-file | notes.orgcaptmpl | |
nN | Novel | Note | file+headline | org-novel-notes-file | notes.orgcaptmpl |
nq | Quote | file+headline | org-default-notes-file | notes-quote.orgcaptmpl | |
nw | Worldbuilding | Note | file+headline | org-wordbuilding-file | notes.orgcaptmpl |
N | Novel | ||||
Ni | Ideas | file+headline | org-novel-notes-file | notes.orgcaptmpl | |
p | Protocol | Link | file+headline | org-default-notes-file | protocol.orgcaptmpl |
r | Resources | ||||
rc | Conlanging | Resource | file+headline | org-conlanging-file | resource.orgcaptmpl |
re | Emacs | file+headline | org-default-notes-file | resource.orgcaptmpl | |
ri | Informatique | file+headline | org-default-notes-file | resource.orgcaptmpl | |
rl | Linguistics | file+headline | org-default-notes-file | resource.orgcaptmpl | |
rw | Worldbuilding | Resource | file+headline | org-wordbuilding-file | resource.orgcaptmpl |
t | Tasks | ||||
tb | Birthday | file+headline | org-private-agenda-file | birthday.orgcaptmpl | |
te | Event | file+headline | org-private-agenda-file | event.orgcaptmpl | |
th | Health | file+headline | org-private-agenda-file | health.orgcaptmpl | |
ti | Informatique | file+headline | org-private-agenda-file | informatique.orgcaptmpl |
The following code snipped is not tangled into my configuration file, but instead creates the equivalent to the table above into EmacsLisp code found in the next code snippet.
(mapconcat (lambda (entry)
(let* ((shortcut (nth 0 entry))
(name (nth 1 entry))
(title (nth 2 entry))
(insertmode (nth 3 entry))
(fileinsert (nth 4 entry))
(sourceorg (nth 5 entry)))
(if (string= "" insertmode)
(format "(\"%s\" \"%s\")" shortcut name)
(concat (format "(\"%s\" \"%s\" entry\n" shortcut name)
(format " (%s %s%s)\n" insertmode fileinsert
(if (string= "file+datetree" insertmode) ""
(format " \"%s\"" (if (string= "" title) name title))))
(format " (file \"~/org/capture/%s\"))" sourceorg)))) )
table "\n")
("e" "Email") ("ew" "Write Email" entry (file+headline org-default-notes-file "Emails") (file "~/org/capture/emails.orgcaptmpl")) ("j" "Journal" entry (file+datetree org-journal-file) (file "~/org/capture/journal.orgcaptmpl")) ("l" "Link") ("ll" "General" entry (file+headline org-default-notes-file "General") (file "~/org/capture/")) ("ly" "YouTube" entry (file+headline org-default-notes-file "YouTube") (file "~/org/capture/youtube.orgcaptmpl")) ("L" "Protocol Link" entry (file+headline org-default-notes-file "Link") (file "~/org/capture/protocol-link.orgcaptmpl")) ("n" "Notes") ("nc" "Conlanging" entry (file+headline org-conlanging-file "Note") (file "~/org/capture/notes.orgcaptmpl")) ("nn" "General" entry (file+headline org-default-notes-file "General") (file "~/org/capture/notes.orgcaptmpl")) ("nN" "Novel" entry (file+headline org-novel-notes-file "Note") (file "~/org/capture/notes.orgcaptmpl")) ("nq" "Quote" entry (file+headline org-default-notes-file "Quote") (file "~/org/capture/notes-quote.orgcaptmpl")) ("nw" "Worldbuilding" entry (file+headline org-wordbuilding-file "Note") (file "~/org/capture/notes.orgcaptmpl")) ("N" "Novel") ("Ni" "Ideas" entry (file+headline org-novel-notes-file "Ideas") (file "~/org/capture/notes.orgcaptmpl")) ("p" "Protocol" entry (file+headline org-default-notes-file "Link") (file "~/org/capture/protocol.orgcaptmpl")) ("r" "Resources") ("rc" "Conlanging" entry (file+headline org-conlanging-file "Resource") (file "~/org/capture/resource.orgcaptmpl")) ("re" "Emacs" entry (file+headline org-default-notes-file "Emacs") (file "~/org/capture/resource.orgcaptmpl")) ("ri" "Informatique" entry (file+headline org-default-notes-file "Informatique") (file "~/org/capture/resource.orgcaptmpl")) ("rl" "Linguistics" entry (file+headline org-default-notes-file "Linguistics") (file "~/org/capture/resource.orgcaptmpl")) ("rw" "Worldbuilding" entry (file+headline org-wordbuilding-file "Resource") (file "~/org/capture/resource.orgcaptmpl")) ("t" "Tasks") ("tb" "Birthday" entry (file+headline org-private-agenda-file "Birthday") (file "~/org/capture/birthday.orgcaptmpl")) ("te" "Event" entry (file+headline org-private-agenda-file "Event") (file "~/org/capture/event.orgcaptmpl")) ("th" "Health" entry (file+headline org-private-agenda-file "Health") (file "~/org/capture/health.orgcaptmpl")) ("ti" "Informatique" entry (file+headline org-private-agenda-file "Informatique") (file "~/org/capture/informatique.orgcaptmpl"))
Below you can find the equivalent code as described above.
(setq
org-capture-templates
'(
<<org-capture-shortcut-gen()>>
))
You may notice a capture entry for my journal, and this is due to the fact I
do not use org-journal
anymore: it was too overpowered for me, and I
prefer to keep it simple with a single file. And as you can see, and unlike
a lot of other Emacs configurations, the content of the template is not set
in the variable, but in external files which can be modified freely as
actual Org buffers instead of trying to get a proper one with loads of \n
characters and such. All these templates are declared below.
In the next sub-sections will be described my org capture templates. These
are not tangled into my Emacs configuration files, but into separate
.orgcaptmpl
files stored into ~/org/capture/
.
Emails
This is my template for a new Email:
** TODO [#A] Write Email
SCHEDULED: %^t
:PROPERTIES:
:CAPTURED: %U
:END:
From: Lucien Cartier-Tilet <lucien@phundrak.com>
To: %^{Recipient}
Subject: %^{Object}
--text follows this line--
%?
--
Lucien “Phundrak” Cartier-Tilet
https://phundrak.com (Français)
https://en.phundrak.com (English)
Sent from a Free and Open-Source Linux operating system with GNU/Emacs
I use it in case my computer is not yet connected to the internet and I
need to already write the email so I can send it later. All I will need to
to afterwards will be to copy and paste my capture in a new message buffer
and send it once I am back online. This is exported to
~/org/capture/email.orgcaptmpl
.
Journal
This template is quite simple: it creates a new entry with the current
timestamp as its title, a brief title of my choosing, and then I can write
whatever I wish to write. This is exported to
~/org/capture/journal.orgcaptmpl
.
* %U %^{Title}
%?
Notes
This template is used for taking note about various subjects that can go
from conlanging to development. I wrote it so I can know from where this
capture was made and when, and it even supports text that was highlighted
in Emacs that will be inserted in a quote block. This is exported to
~/org/capture/notes.orgcaptmpl
.
* %^{Title}
:PROPERTIES:
:CAPTURED: %U
:END:
%?
* %^{Title}
:PROPERTIES:
:CAPTURED: %U
:END:
Possible inspiration:
#+begin_quote
%i
#+end_quote
%?
Protocol
This capture is used when received through org-protocol, with the
Org-protocol Extension for Firefox. It allows me to save in a quote block
what I’ve highlighted, as well as the link of the webpage on which my saved
content was highlighted. This file is exported to
~/org/capture/protocol.orgcaptmpl
.
* TODO [#C] %^{Title}
:PROPERTIES:
:CAPTURED: %U
:LINK: %:link
:TITLE: %:description
:END:
#+begin_quote
%i
#+end_quote
%?
This next capture template is used only when a link is sent to Emacs and no content was highlighted.
* TODO [#C] Link: %^{Title}
:PROPERTIES:
:CAPTURED: %U
:LINK: %:link
:TITLE: %:description
:END:
%?
Resources
This is the default template for resources, which generally are located on the Internet. By default, I give them the lowest priority, because although this is something for me to remember later, it is not by default important. You can see in the properties I record when the capture happened, and what the link is. The title of the capture is a summary of what this is, while the body of the capture is a more detailed explanation of what I capture, why, and how it could be useful to me.
* TODO [#C] %^{Title}
:PROPERTIES:
:CAPTURED: %U
:LINK: %^{Link}
:END:
%?
Tasks
One type of task I often capture is related to my servers or thing about computers in general. With this, I can capture a task for which I will either set a schedule or a deadline.
* TODO %^{Title}
%^{Scheduled or Deadline?||SCHEDULED||DEADLINE}: %^t
:PROPERTIES:
:CATEGORY: %^{Category}
:END:
%?
This capture is rarely used (I’m lucky to have a good health), but it can be useful.
* %^{Title}
SCHEDULED: %^t
%?
This capture is used to store new birthdays I have to remember. They are set to be repeated yearly.
* %^{Name}
SCHEDULED: %^t
* %^{Title}
%^{Scheduled or deadline?||SCHEDULED||DEADLINE}: %^t
%?
Links
* TODO [#C] %^{Title}
:PROPERTIES:
:CAPTURED: %U
:LINK: %^{Link}
:END:
%?
* TODO [#C] %^{Title}
:PROPERTIES:
:CAPTURED: %U
:AUTHOR: %^{Author}
:LINK: %^{Link}
:END:
%?
Org projects
Another great features of Org-mode is the Org projects that allow the user to easily publish a bunch of org files to a remote location. Here is the current declaration of my projects, which will be detailed later:
<<org-proj-config-setup>>
<<org-proj-lang-setup>>
(setq org-publish-project-alist
`(
<<org-proj-config-html>>
<<org-proj-config-static>>
<<org-proj-config>>
<<org-proj-lang-html>>
<<org-proj-lang-pdf>>
<<org-proj-lang-static>>
<<org-proj-lang>>))
Configuration website
This is my configuration for exporting my dotfiles to my website in a web format only. No PDFs or anything, just HTML. Please note that I do not use that often anymore, I much prefer the automatic script that I have which deploys through my Drone instance my website on git pushes.
And before we get into the actual configuration, I would like to introduce a couple of variables. This is a bit more verbose than if I declared everything manually, but now I can change all three values at the same time without a hasle.
(setq phundrak//projects-config-target "/ssh:Tilo:~/www/phundrak.com/config"
phundrak//projects-config-source "~/org/config/"
phundrak//projects-config-language "en"
phundrak//projects-config-recursive t)
Now, here is my configuration. In this snippet, my org files located in my source directory get exported in the HTML format and published to my target directory on my remote server through SSH via TRAMP. A sitemap is automatically generated, which comes in handy with the online sitemap that is available through the navigation bar.
("config-website-org"
:base-directory ,phundrak//projects-config-source
:base-extension "org"
:publishing-directory ,phundrak//projects-config-target
:recursive ,phundrak//projects-config-recursive
:language ,phundrak//projects-config-language
:publishing-function org-html-publish-to-html
:headline-levels 5
:auto-sitemap t
:auto-preamble t)
We also have the component for all the static files needed to run the website (mostly images tbh).
("config-website-static"
:base-directory ,phundrak//projects-config-source
:base-extension "png\\|jpg\\|gif\\|webp\\|svg\\|jpeg\\|ttf\\|woff\\|txt\\|epub\\|md"
:publishing-directory ,phundrak//projects-config-target
:recursive ,phundrak//projects-config-recursive
:language ,phundrak//projects-config-language
:publishing-function org-publish-attachment)
The project is then defined like so:
("config-website"
:components ("config-website-org"
"config-website-static"))
Linguistics website
My linguistics website is made out of three projects. As for the previous project, let’s declare the common values for these.
(setq phundrak//projects-conlanging-target "/ssh:Tilo:~/www/phundrak.com/langue/"
phundrak//projects-conlanging-source "~/Documents/conlanging/content/"
phundrak//projects-conlanging-language "fr"
phundrak//projects-conlanging-recursive t)
The first component is the one generating the HTML files from the org files.
("langue-phundrak-com-org"
:base-directory ,phundrak//projects-conlanging-source
:base-extension "org"
:exclude "\\./\\(CONTRIB\\|README\\|head\\|temp\\|svg-ink\\).*"
:publishing-directory ,phundrak//projects-conlanging-target
:recursive ,phundrak//projects-conlanging-recursive
:language ,phundrak//projects-conlanging-language
:publishing-function org-html-publish-to-html
:headline-levels 5
:auto-sitemap t
:auto-preamble t)
We also have the component for the LaTeX and PDF part of the website:
("langue-phundrak-com-pdf"
:base-directory ,phundrak//projects-conlanging-source
:base-extension "org"
:exclude "\\./\\(CONTRIB\\|README\\|index\\|head\\|temp\\|svg-ink\\).*"
:publishing-directory ,phundrak//projects-conlanging-target
:recursive ,phundrak//projects-conlanging-recursive
:language ,phundrak//projects-conlanging-language
:publishing-function org-latex-publish-to-pdf
:headline-levels 5
:auto-preamble t)
And lastly, we have the component for all the static files needed to run the website:
("langue-phundrak-com-static"
:base-directory ,phundrak//projects-conlanging-source
:base-extension "png\\|jpg\\|gif\\|webp\\|svg\\|jpeg\\|ttf\\|woff\\|txt\\|epub"
:publishing-directory ,phundrak//projects-conlanging-target
:recursive ,phundrak//projects-conlanging-recursive
:language ,phundrak//projects-conlanging-language
:publishing-function org-publish-attachment)
The project is then defined like so:
("langue-phundrak-com"
:components ("langue-phundrak-com-org"
"langue-phundrak-com-static"
"langue-phundrak-com-pdf"))
Editing and modes
Default modes
:CUSTOM_ID: User_Configuration-Miscellaneous-Default_modes-b9e1522c
:END:
Some buffers sometimes won’t have a default mode at all, such as the
*scratch*
buffer. In any vanilla configuration, they will then default to
text-mode
. I personally prefer org-mode
to be my default mode, so let’s
set it so!
(setq edit-server-default-major-mode 'org-mode)
Evil
:CUSTOM_ID: User_Configuration-Miscellaneous-Evil-ab8a36e3
:END:
As a user of Evil, I’m sometimes pissed when I accidentally press C-u
and
it gets me to the top of the document. So, let’s disable it:
(setq evil-want-C-u-scroll nil)
File extensions
Sometimes, Emacs doesn’t recognize or misrecognizes some extensions, resulting in a wrong mode set for said file. Let’s fix that by associating the extension with the desired mode:
(dolist (e '(("xml" . web-mode)
("xinp" . web-mode)
("aiml" . web-mode)
("C" . c++-mode)
("dconf" . conf-mode)
("yy" . bison-mode)
("ll" . flex-mode)
("s" . asm-mode)
("pl" . prolog-mode)
("l" . scheme-mode)
("vs" . glsl-mode)
("fs" . glsl-mode)))
(push (cons (concat "\\."
(car e)
"\\'") (cdr e))
auto-mode-alist))
We also have a couple of extensions which should all be in conf-unix-mode
,
let’s indicate that to Emacs:
(dolist (e '("service" "timer" "target" "mount" "automount"
"slice" "socket" "path" "netdev" "network"
"link"))
(push (cons (concat "\\." e "\\'") 'conf-unix-mode)
auto-mode-alist))
Hooks
I also have some hooks I use for enabling some major and minor modes. The first one here allows the execution of the deletion of trailing space each time I save a file.
(add-hook 'before-save-hook 'delete-trailing-whitespace)
I also want to always be in visual-line-mode
so Emacs soft-wraps lines
that are too long for the buffer they are displayed in. This will also be
enabled for Elfeed.
(add-hook 'prog-mode-hook 'visual-line-mode)
(add-hook 'elfeed-read-mode-hook 'visual-line-mode)
I also want for some non-programming modes to enable a hard-limit in terms
of how many characters can fit on one line. The modes that benefit from that
are message-mode
, org-mode
, text-mode
and markdown-mode
.
(mapc (lambda (x)
(add-hook x 'auto-fill-mode)
(add-hook x 'visual-line-mode))
'(message-mode-hook
org-mode-hook
text-mode-hook
markdown-mode-hook))
Twittering mode
For twittering-mode
, a Twitter major mode for Emacs, I want to encrypt my
data using a master password, which I do thanks to this option:
(setq twittering-use-master-password t)
Wrapping regions
I really like the M-(
keybinding for wrapping a selected region between
parenthesis. However, parenthesis are not everything (even in Lisp
dialects), and other wrappers could be nice. And they are! Here is how they
are declared:
(global-set-key (kbd "M-[") 'insert-pair)
(global-set-key (kbd "M-{") 'insert-pair)
(global-set-key (kbd "M-<") 'insert-pair)
(global-set-key (kbd "M-'") 'insert-pair)
(global-set-key (kbd "M-`") 'insert-pair)
(global-set-key (kbd "M-\"") 'insert-pair)
For the record, this is from Howard Abram’s dotfiles.
Keybindings
As you will see, I defined a LOT of custom keybindings. All of them are
Spacemacs keybindings, defined in a way they can be used seamlessly with
Evil. They almost all begin with o
, which is a prefix reserved for
user-defined keybindings so they won’t conflict with any package. Let’s
declare it like so.
(spacemacs/declare-prefix "o" "custom")
Now, all keybindings that will be defined can be invoked in Normal-mode with
the SPC
key followed by the sequence assigned to each keybinding.
Before some more specialized categories, I have two commands which don’t fit
into any other category that I sometime use. The first one is a fix for the
Bépo keybindings which left out a keybind: winum-select-window-by-number
is
still bound to SPC ²
, which is not a key that is available on the bépo
layout (instead, we use the dead key ^
followed by 2
, or any digits). So
instead, let’s use the key that is physically in the same place: $
.
(spacemacs/declare-prefix "$" "select window by number")
(spacemacs/set-leader-keys "$" 'winum-select-window-by-number)
The following, I use it rarely, it can launch an external command from Emacs to launch, for instance, my web browser or any other software not related to Emacs. It offers a similar interface to dmenu through helm.
(spacemacs/declare-prefix "or" "external command")
(spacemacs/set-leader-keys "or" 'helm-run-external-command)
Applications
As this is a new category, let’s declare its prefix:
(spacemacs/declare-prefix "oa" "applications")
Now, let’s also declare the keybindings in this category. oac
will invoke
Emacs’ calculator, while oac
invokes the calendar, oae
invokes the Eww
navigator and oaw
invokes the weather forecast.
(spacemacs/set-leader-keys
"oac" 'calc
"oaC" 'calendar
"oae" 'eww
"oaw" 'wttrin)
Org tree slide
Finally, here we have the keybindings for org-tree-slide
, a presentation
mode with orgmode. Since I want the keys to be directly accessible without
any prefix from Spacemacs, I’ll have to declare them the vanilla way. First
we have keybindings that will launch the presentation:
(define-key org-mode-map (kbd "<f8>") 'org-tree-slide-mode)
(define-key org-mode-map (kbd "s-<f8>") 'org-tree-slide-skip-done-toggle)
Next, we have some additional keybindings that will only be active when in
org-tree-slide-mode
. The first one will allow us to exit this mode, while
the second one will toggle the display of headers marked as DONE
. Next,
we have F9
and F10
which are bound to movement in the slide, while
F11
changes the way the content is displayed. We also set
org-tree-slide-skip-outline-level
to set the maximum depth we will
display as an individual heading during the presentation.
(when (require 'org-tree-slide nil t)
(global-set-key (kbd "<f8>") 'org-tree-slide-mode)
(global-set-key (kbd "S-<f8>") 'org-tree-slide-skip-done-toggle)
(define-key org-tree-slide-mode-map (kbd "<f9>")
'org-tree-slide-move-previous-tree)
(define-key org-tree-slide-mode-map (kbd "<f10>")
'org-tree-slide-move-next-tree)
(define-key org-tree-slide-mode-map (kbd "<f11>")
'org-tree-slide-content)
(setq org-tree-slide-skip-outline-level 4)
(org-tree-slide-narrowing-control-profile)
(setq org-tree-slide-skip-done nil))
Comments
Some keybindings are also related to comment editing, in particular using outorg. Let’s first declare the dedicated prefix:
(spacemacs/declare-prefix "oc" "comments")
Now, let’s declare the following keybindings:
(spacemacs/set-leader-keys
"occ" 'outorg-copy-edits-and-exit
"oce" 'outorg-edit-as-org
"oco" 'outline-minor-mode)
oco
enables the outline minor mode, which then allows for the edition of
comments in org buffers with oce
and saving them to the original source
file with occ
.
Files
(spacemacs/declare-prefix "of" "open org file")
(spacemacs/set-leader-keys "of" 'phundrak/find-org-file)
I also have a shortcut for helm-locate
in case I need to find a file that
is not in these directories. One advantage of this over helm-find
is that
it doesn’t matter from where I call it, it will find any file on my system
that matches the query, whereas helm-find
will only search in the current
directory and its subdirectories. This time, the declaration is much
simpler:
(spacemacs/declare-prefix "oF" "locate file")
(spacemacs/set-leader-keys "oF" 'helm-locate)
And that’s it! This should list all my org files under these directories and give me fuzzy finding for these files. I just need to partially type the name of the file I want to open and it should open without any issue.
Multiple cursors
I don’t really like Spacemacs’ layer for MultipleCursors, so I prefer to simply install the package and create shortcuts for it myself. Let’s first declare category:
(spacemacs/declare-prefix "om" "multiple-cursors")
Now, let’s declare the shortcuts related to multiple-cursors:
(spacemacs/set-leader-keys
"ome" 'mc/edit-lines
"omn" 'mc/mark-next-like-this
"omp" 'mc/mark-previous-like-this
"oma" 'mc/mark-all-like-this)
Org-mode
Now, onto some shortcuts related to org-mode. Let’s first declare the category:
(spacemacs/declare-prefix-for-mode 'org-mode "mo" "custom" "User-defined keybindings")
(spacemacs/declare-prefix-for-mode 'org-mode "mot" "toggle" "Toggle org elements")
(spacemacs/declare-prefix-for-mode 'org-mode "moT" "tables")
Now, I have a couple of shortcuts I use regularly:
(spacemacs/set-leader-keys-for-major-mode 'org-mode
"os" 'org-insert-structure-template
"ots" 'phundrak/toggle-org-src-window-split
"ott" 'org-sidebar-tree)
(spacemacs/declare-prefix-for-mode 'org-mode "moS" "insert template")
(spacemacs/declare-prefix-for-mode 'org-mode "mots" "toggle src split")
os
allows me to insert an org structure template defined in
org-structure-template-alist
(see Org behavior), while ott
displays the
outline of the current org file.
oT
is the prefix for tree-related operations:
(spacemacs/declare-prefix-for-mode 'org-mode "moT" "tables")
These shortcuts allow to manipulate the width of the column the cursor is currently in, by either shrinking it, expanding it, or toggling its state between shrunk or expanded. A prefix for all of these commands has been also added in order to make the purpose of the shortcuts clearer.
(spacemacs/set-leader-keys-for-major-mode 'org-mode
"oTt" 'org-table-toggle-column-width
"oTe" 'org-table-expand
"oTs" 'org-table-shrink)
(spacemacs/declare-prefix-for-mode 'org-mode "moTt" "toggle width")
(spacemacs/declare-prefix-for-mode 'org-mode "moTe" "expand")
(spacemacs/declare-prefix-for-mode 'org-mode "moTs" "shrink")
Finaly, I set the following shortcut in order to easily remove RESULTS
blocks from org source code blocks:
(spacemacs/set-leader-keys-for-major-mode 'org-mode
"or" 'org-babel-remove-result-one-or-many)
(spacemacs/declare-prefix-for-mode 'org-mode "mor" "remove org result")
Toggle
This category allows to toggle some modes and options.
(spacemacs/declare-prefix "ot" "toggle")
As you can see, I have here four shortcuts for toggling various elements in Emacs:
-
otb
- toggles
fancy-battery-mode
. This comes in very handy when I am on a laptop that is not pluged in or which is charging. -
otd
- toggles
elcord-mode
. This mode is used to create an Emacs rich integration in Discord. -
otf
- toggles the activation of FlyCheck, Emacs’ spell checker. It is by default disabled, and I can turn it on with this shortcut only when needed.
-
ots
- toggles
prettify-symbols-mode
. This allows Emacs to replace some symbols by some others, like for example by replacinglambda
in Emacs Lisp buffers with an actual λ.
(spacemacs/set-leader-keys
"otb" 'fancy-battery-mode
"otd" 'elcord-mode
"otf" 'flycheck-mode
"ots" 'prettify-symbols-mode)
We also have some input methods-related shortcuts in a sub-category: oti
.
The first shortcuts below are used to either toggle between no input method
or the last one used (otit
), or choose an input method among the various
available ones from Emacs (otis
).
(spacemacs/declare-prefix "oti" "input methods")
(spacemacs/set-leader-keys
"otit" 'toggle-input-method
"otis" 'set-input-method)
The shortcuts below though allow me to directly switch to one of these three known input methods I sometimes or often use, namely Japanese, Tibetan and IPA (by typing in X-SAMPA).
(spacemacs/declare-prefix "otij" "Japanese")
(spacemacs/declare-prefix "otix" "IPA (X-SAMPA)")
(spacemacs/declare-prefix "otiT" "Tibetan")
(spacemacs/set-leader-keys
"otij" (lambda () (interactive) (set-input-method 'japanese))
"otix" (lambda () (interactive) (set-input-method 'ipa-x-sampa))
"otiT" (lambda () (interactive) (set-input-method 'tibetan-wylie)))
Text
The last category is a text-related category. Let’s declare it:
(spacemacs/declare-prefix "ox" "text")
The only command for now is a command that allows the use of C-u M-q
with
the simple shortcut oxf
:
(spacemacs/set-leader-keys "oxf" 'phundrak/fill-paragraph)
Mu4e
Mu4e is a frontend for mu, an email analyzer which sits on top of a Maildir
which gets updated with the mbsync
command from isync
. It has a lot of
neat features, but I guess my favorite ones are:
- the search query feature
- rendering an HTML email in the browser
Due to mu sitting on top of a maildir, I need to tell mu4e where said maildir is, and point it the trash, archive, and sent folders as well as the refresh command and how frequently I want my emails to be refreshed.
(setq mu4e-maildir "~/.mail"
mu4e-trash-folder "/Trash"
mu4e-refile-folder "/Archive"
mu4e-sent-folder "/Sent"
mu4e-drafts-folder "/Drafts"
mu4e-get-mail-command "mbsync -a"
mu4e-update-interval 60)
The following also allows me to automatically include my signature in my Emails, to view images in my Emacs buffers and to show me the address of my contacts and not just their names.
(setq mu4e-compose-signature-auto-include t
mu4e-view-show-images t
mu4e-view-prefer-html t
mu4e-view-show-addresses t)
This source block is an example of the search queries in mu4e, and part of
the reason why I very much like mu4e: these bookmarks are actually defined by
search queries, but act as if they were just yet another type of custom inbox
you get with modern Email client (and often you don’t even get them). All
these bookmarks can be accessed through a shortcut on the main mu4e buffer,
prefixed by b
. So, for instance, my unread messages are accessed through
bU
.
(setq mu4e-bookmarks
`(("maildir:/Inbox AND NOT flag:trashed" "Inbox" ?i)
(,(s-join " "
'("maildir:/Inbox"
"AND NOT flag:trashed"
"AND (f:/.*up8\.edu|.*univ-paris8.*/"
"OR c:/.*up8\.edu|.*univ-paris8.*/"
"OR t:/.*up8\.edu|.*univ-paris8.*/)"))
"University" ?u)
(,(s-join " "
'("maildir:/Inbox"
"AND (list:ateliers-emacs.framalistes.org"
"OR list:ateliers-paris.emacs-doctor.com)"))
"Emacs" ?e)
("maildir:/Sent" "Sent messages" ?s)
("flag:unread AND NOT flag:trashed" "Unread messages" ?U)
("date:today..now AND NOT flag:trashed" "Today's messages" ?t)
("date:7d..now AND NOT flag:trashed" "Last 7 days" ?w)
("date:1m..now AND NOT flag:trashed" "Last month" ?m)
("date:1y..now AND NOT flag:trashed" "Last year" ?y)
("flag:trashed AND NOT flag:trashed" "Trash" ?T)
("mime:image/* AND NOT flag:trashed" "Messages with images" ?p)))
On new email arrival, Emacs can send the system a notification which will be handled as any other notification received by the system and will display the number of unread emails to the user; in my case, notifications are handled by AwesomeWM.
(setq mu4e-enable-notifications t
mu4e-alert-email-notification-types '(count))
(with-eval-after-load 'mu4e-alert
(mu4e-alert-set-default-style 'notifications))
(add-hook 'mu4e-view-mode-hook 'visual-line-mode)
Now this hook is added so I can get a maximal width for the text of my
emails, I really don’t like it when lines are kilometers long. I would like
instead to hook visual-line-mode
and auto-fill-mode
, but for some reasons
Emacs throws an error when I add them, So I go with visual-fill-column-mode
instead.
(add-hook 'mu4e-view-mode-hook 'visual-fill-column-mode)
On modern-day computers, with wide screens almost everywhere, there is no reason for the email buffer to open below the email directory and not on its right, which is why I set the split view to be vertical instead of horizontal. And to make it more readable, the header window will only occupy 40% of Emacs’ frame, the rest will be given to emails. As you can see, the width of the mu4e headers is evaluated each time we enter it, so it can react to the frame being potentially not the same width than earlier or the window not taking the entire frame. It has to have a minimal size though, I’d say 80 characters is enough for that.
(setq mu4e-split-view 'vertical)
(add-hook 'mu4e-headers-mode-hook
(lambda ()
(setq mu4e-headers-visible-columns (max 80
(round (* (window-width)
0.4))))))
This is the setup I have for my SMTP mail server: I point Emacs’ SMTP services to my private mail server on its SMTP port, which should be used with a STARTTLS stream. And I tell Emacs this is the default way to send an email.
(setq smtpmail-smtp-server "mail.phundrak.com"
smtpmail-smtp-service 587
smtpmail-stream-type 'starttls
message-send-mail-function 'smtpmail-send-it)
I am unsure yet if this has any effect on mu4e, but this variable should discourage mu4e from reading rich text emails and instead open them as plain text. However, I do not wish to discourage opening HTML emails since I can compile them to PDF or open them in the browser.
(setq mm-discouraged-alternatives '("text/richtext"))
I am still unsure about this variable and if it has an effect on mu4e, but I wish to set a default web viewer for my HTML emails: w3m. This is not as effective as sending the email in the browser or rendering it as a PDF file, but it can be effective enough for some emails.
(setq mm-text-html-renderer 'w3m)
Miscellaneous
I have a lot of variables that need to be set but don’t fall in any other category, so I’ll collect them here.
I have this regexp for detecting paragraphs.
(setq paragraph-start "\f\\|[ \t]*$\\|[ \t]*[-+*] ")
Pinentry
Pinentry should use the loopback
mode when communicating with GnuPG. Let’s
set it so:
(setq epa-pinentry-mode 'loopback)
Wttr.in cities
Thanks to the wttrin package, I can get the weather forecast in Emacs for a couple of cities. I just need to specify them to Emacs like so:
(setq wttrin-default-cities '("Aubervilliers" "Paris" "Lyon" "Nonières"
"Saint Agrève"))
:PROPERTIES:
:PROPERTIES:
Visual configuration
Battery mode line
I want to see by default how much battery my computer has, so let’s enable it:
(spacemacs/toggle-mode-line-battery-on)
Prettified symbols
Just because it is pleasing to the eye, some symbols in source code get prettified into simpler symbols. Here is the list of symbols that are to be prettified. You can see in the corresponding comment what symbol will be displayed.
(setq prettify-symbols-alist '(("lambda" . 955) ; λ
("->" . 8594) ; →
("<->" . 8596) ; ↔
("<-" . 8592) ; ←
("=>" . 8658) ; ⇒
("<=>" . 8860) ; ⇔
("<=" . 8656) ; ⇐
("mapc" . 8614) ; ↦
("map" . 8614) ; ↦
(">>" . 187) ; »
("<<" . 171) ; «
))
Let’s enable this mode globally.
(global-prettify-symbols-mode 1)
Time in modeline
I like to see what time it is in Emacs. It may be sometimes redundant with my window manager’s clock, but the latter is not always visible; I might have, for instance, Emacs in fullscreen. First, I need to tell Emacs a few things: I want a 24h format, as well as the current date.
(setq display-time-24hr-format t
display-time-day-and-date t)
Now that Emacs knows how to display time, let’s ask it to.
(display-time-mode 1)
Nov-mode
nov-mode
is the mode used in the Epub reader. Here I will write a little
function that I will call through a hook each time I’m opening a new EPUB
file.
(defun my-nov-font-setup ()
(face-remap-add-relative 'variable-pitch :family "Charis SIL"
:size 16
:height 1.0))
Let’s bind this function to the nov-mode
hook. By the way, we’ll also
enable the visual-line-mode
here, just in case.
(mapc (lambda (mode)
(add-hook 'nov-mode-hook mode))
'('my-nov-font-setup 'visual-line-mode))
Let’s also set the maximum length of the lines in nov-mode
:
(setq nov-text-width 80)
Programming
LSP
When it comes to the LSP layer, there are some options which are not enabled
by default that I want to use, especially some modes I want to take advantage
of. This is why I enable first the lsp-treemacs-sync-mode
so treemacs is
LSP aware:
(lsp-treemacs-sync-mode 1)
I also enable some layers related to dap
, the Debug Adapter Protocol, which
works really nicely with LSP. Let’s enable Dap’s modes:
(dap-mode 1)
(dap-ui-mode 1)
(dap-tooltip-mode 1)
Finally, I also want the documentation tooltip to show up when the cursor is above a documented piece of code or symbol. Let’s enable that too:
(tooltip-mode 1)
ASM configuration
The first thing I will set with my ASM configuration is where the reference PDF is located.
(setq x86-lookup-pdf "~/Documents/code/asm/Intelx86/325383-sdm-vol-2abcd.pdf")
I will also modify what the comment character is, from a ;
to a #
:
(setq asm-comment-char ?\#)
C/C++
As the C/C++ syntax is checked by flycheck, let’s make sure we are using the latest standard available, that is C++17 and C17, from Clang.
(add-hook 'c-mode-hook
(lambda ()
(setq flycheck-clang-language-standard "c17")))
(add-hook 'c++-mode-hook
(lambda ()
(setq flycheck-clang-language-standard "c++17")))
Dart configuration
For Dart, I mainly declared some custom shortcuts bound to dart-mode
related to flutter, so nothing too exciting here. Some prefix are declared in
order to avoid the shortcuts in helm to show up as just custom
.
(spacemacs/declare-prefix-for-mode 'dart-mode "mo" "user-defined")
(spacemacs/declare-prefix-for-mode 'dart-mode "mof" "flutter")
(spacemacs/declare-prefix-for-mode 'dart-mode "mofr" "flutter-run")
Now, for the shortcuts themselves:
(spacemacs/set-leader-keys-for-major-mode 'dart-mode
"ofH" 'flutter-hot-restart
"ofh" 'flutter-hot-reload
"ofq" 'flutter-quit
"ofr" (lambda () (interactive) (flutter-run "-v"))
"ofs" 'flutter-screenshot)
Emacs Lisp
Here will be stored my configuration directly related to Emacs Lisp, including some functions or default modes.
Enable eldoc-mode
by default
By default, if some Elisp code is opened, I want to enable eldoc-mode
so I
can easily get some documentation on the symbols in the source code. This is
done via the use of hooks.
(add-hook 'prog-mode-hook 'eldoc-mode)
phundrak/write-to-buffer
I was very surprised when I discovered no such function exists in Elisp. This function basically writes a string into a buffer, and optionally switches the user to the buffer. Here is the code for that function:
(defun write-to-buffer ($input-string $outputbuf &optional $switchbuf)
"Writes `$input-string' to the specified `output-buffer'. If
`switch-buffer' is non-nil, the active buffer will switch to the
output buffer; otherwise, it will take the user back to their
initial buffer. Works with `$input-string' as a string or a list
of strings."
(let ((oldbuf (current-buffer)))
(switch-to-buffer $outputbuf)
(cond ((char-or-string-p $input-string) (insert $input-string))
((listp $input-string) (dolist (elem $input-string)
(insert (format "%s\n" elem)))))
(if (not $switchbuf)
(switch-to-buffer oldbuf))))
Python
Emacs throws me an error about the python interpreter, let’s silence it:
(setq python-shell-completion-native-disabled-interpreters '("python"))
Rust
I need to point to racer where the source code of Rust is located so I can
get some documentation. This is installed with the rust-src
component you
can get through rustup
. To install it, simply run
$ rustup component add rust-src
Now, the source code for Rust should be included in your installation. I personally prefer to develop with Rust stable, so let’s indicate to Emacs to search for documentation in the stable sources:
(setq racer-rust-src-path
"~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src")
Rust’s default cargo check
command is already very good, however I also
enjoy getting some more hints while developping, and clippy
does a very
good job at it. To get clippy, I need to run the following to install it:
$ rustup compontent add clippy
And this will get it installed with all of my Rust toolchain, and it will be
updated with it. Now, let’s indicate LSP that I want to use that instead of
check
:
(setq lsp-rust-analyzer-cargo-watch-command "clippy")
Finally, I wish to enable electric-pair-mode
and indent-guide-mode
for
Rust files, so let’s enable that through the use of a hook:
(add-hook 'rust-mode-hook
'(lambda ()
(local-set-key (kbd "TAB") #'company-indent-or-complete-common)
(electric-pair-mode 1)
(indent-guide-mode 1)))
Scheme
The Scheme configuration will be very short, I just need to tell Emacs the name of the interpreter since it is not the default one:
(setq geiser-chicken-binary "chicken-csi")
Projectile
Projectile is an awesome utility which helps managing projects within Emacs.
It will automatically detect version controlled directories, and will by
default assume this is a project I can be working on. However, there are some
directories that are version controlled that I do not want to see in my list
of projects, namely all the cached AUR packages from my AUR helper, yay
.
They are all stored in the same parent directory, so let’s ignore that. I
will also make Emacs ignore all node_modules
directories it could
encounter. And for some reason, ~/.config/emacs
is always in my projects
list (I now use XDG-compliant directories), so let’s also ignore that.
(phundrak/add-all-to-list projectile-globally-ignored-directories
"~/.cache/yay/*" "node_modules" "~/.config/emacs")
Security
This paragraph is about making Emacs and GPG as a whole (since Emacs is
always open on my computer) more secure. The first thing I want to make is
a function that will close any buffer that contains an open .gpg
file –I
certainly do not want anyone to be able to read such files on my computer if
I leave it even for a couple of minutes.
(defun phundrak/kill-gpg-buffers ()
"Kill GPG buffers."
(interactive)
(let ((buffers-killed 0))
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (string-match ".*\.gpg$" (buffer-name buffer))
(message "Auto killing .gpg buffer '%s'" (buffer-name buffer))
(when (buffer-modified-p buffer)
(save-buffer))
(kill-buffer buffer)
(setq buffers-killed (+ buffers-killed 1)))))
(unless (zerop buffers-killed)
;; Kill gpg-agent.
(shell-command "gpgconf --kill gpg-agent")
(message "%s .gpg buffers have been autosaved and killed" buffers-killed))))
Notice the (shell-command "gpgconf --kill gpg-agent")
command there: it
kills gpg-agent
which will always respawn each time GPG2 is invoked. That
way, I know anyone trying to open a GPG file will have to insert my password
when trying to do so instead of just hoping I entered it not long ago and
they won’t have to.
But surely, if I only define this function and hope to call it each time I leav my computer, surely at one point I will forget to execute it before leaving. I can’t trust myself to always call it manually. Which is why I’ll ask Emacs itself to call it after it detects a minute of idling. It may become from times to times a bit of a pain, but at least I’m now sure I won’t ever have to worry about someone reading my GPG files open in Emacs while I’m out for a quick break.
(run-with-idle-timer 60 t 'phundrak/kill-gpg-buffers)
Snippets
Yasnippet’s snippets tool is extremely powerful and allows me to write very
quickly code. For now, we have snippets for two modes. The files you’ll see
below are exported to $HOME/.config/emacs/private/snippets/
and to their
respective mode directory. For instance, my caption
snippet for org-mode
will be exported to $HOME/.config/emacs/private/snippets/org-mode/caption
.
Be aware that on top of these custom snippets, I also use the package yasnippet-snippets which provide plenty of already made snippets.
Rust snippets
I have so far two snippets, the first one is actually just a convenience to
make it easier to type a println!
macro than the default snippet.
# -*- mode: snippet -*-
# name: println
# key: pln
# --
println!("${1:{}}", $2);
The second one is more interesting: it is used to create a new
method for
a struct, and it will try to create a function that will assign each
argument passed to the method to members of the struct. It relies on the
custom function I wrote here.
# -*- mode: snippet -*-
# name: new
# key: _new
# --
fn new(${1:args}) -> Self {
$0
Self {
${1:$(phundrak/yas-rust-new-assignments yas-text)}
}
}
Org headers
The first two snippets are used to add HTML or LaTeX attributes to elements
in org-mode. The third also has a similar usage, inserting a #+CAPTION
header before an element, as well as the fourth which inserts a #+NAME
header.
# -*- mode: snippet -*-
# name: ATTR HTML
# key: <ah
# --
,#+ATTR_HTML: $0
# -*- mode: snippet -*-
# name: ATTR LATEX
# key: <al
# --
,#+ATTR_LATEX: $0
# -*- mode: snippet -*-
# name: caption
# key: <ca
# --
,#+CAPTION: $0
# -*- mode: snippet -*-
# name: name
# key: <na
# --
,#+NAME: $0
Now, the following is a bit more complex: it is meant to be used as a new org buffer is created. It will insert an org header for the title, which will default to the buffer’s name capitalized minus the dashes or underscores replaced with spaces, it will insert a default author and email based on the user’s parameters, and the date at the moment of the creation of these headers. The user can also add some tags if they wish to.
# -*- mode: snippet -*-
# name: header
# key: header
# --
,#+TITLE: ${1:`(replace-regexp-in-string "-" " " (capitalize (file-name-nondirectory (file-name-sans-extension (buffer-file-name)))))`}
,#+AUTHOR: `(user-full-name)`
,#+EMAIL: `user-mail-address`
,#+DATE: `(format-time-string "%Y-%m-%d")`
,#+TAGS: $2
$0
Org blocks
Now, Let’s write some snippets for org blocks. The first one is for a comment block, then two snippets for an unnamed and a named Elisp source block, and two others for an unnamed and a named Python source block. There are also two block for generic unnamed source blocks and generic named source blocks.
# -*- mode: snippet -*-
# name: comment block
# key: <co
# --
,#+BEGIN_COMMENT
$0
,#+END_COMMENT
# -*- mode: snippet -*-
# name: emacs-lisp block
# key: <el
# --
,#+BEGIN_SRC emacs-lisp
$0
,#+END_SRC
# -*- mode: snippet -*-
# name: named emacs-lisp block
# key: <eln
# --
,#+NAME: $1
,#+BEGIN_SRC emacs-lisp
$0
,#+END_SRC
# -*- mode: snippet -*-
# name: python block
# key: <py
# --
,#+BEGIN_SRC python
$0
,#+END_SRC
# -*- mode: snippet -*-
# name: named python block
# key: <pyn
# --
,#+NAME: $1
,#+BEGIN_SRC python
$0
,#+END_SRC
# -*- mode: snippet -*-
# name: source block
# key: <s
# --
,#+BEGIN_SRC $1
$0
,#+END_SRC
# -*- mode: snippet -*-
# name: named source block
# key: <sn
# --
,#+NAME: $1
,#+BEGIN_SRC $2
$0
,#+END_SRC
Org misc
Finally, there are a couple of miscellaneous org snippets that insert macros I often use in my conlanging documents. The first one inserts a phonetics macro, while the second one inserts a macro used for my Proto-Ñyqy language.
# -*- mode: snippet -*-
# name: phon
# key: <ph
# --
\{\{\{phon($1)\}\}\} $0
# -*- mode: snippet -*-
# name: nyqy
# key: <ny
# --
\{\{\{nyqy($1)\}\}\} $0
Yadm
yadm is the utility I use for managing my dotfiles, and it is a wrapper In order to manage my dotfiles, I use the following shortcut to launch Magit Status for yadm:
(spacemacs/declare-prefix "oy" "yadm status")
(spacemacs/set-leader-keys "oy" (lambda () (interactive) (magit-status "/yadm::")))
around git
. Logically, it means Magit could theoretically manage my yadm
repo. And it is indeed possible, according to this page using TRAMP. I just
need to add the following code snippet:
(add-to-list 'tramp-methods
'("yadm"
(tramp-login-program "yadm")
(tramp-login-args (("enter")))
(tramp-login-env (("SHELL")
("/bin/sh")))
(tramp-remote-shell "/bin/sh")
(tramp-remote-shell-args ("-c"))))