All checks were successful
deploy / build (push) Successful in 2m22s
443 lines
17 KiB
Org Mode
443 lines
17 KiB
Org Mode
#+title: Emacs — Basic Configuration
|
||
#+setupfile: ../headers
|
||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/basic-config.el
|
||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||
|
||
* Basic Configuration
|
||
** Early Init
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle ~/.config/emacs/early-init.el :mkdirp yes
|
||
:header-args:emacs-lisp+: :exports code :results silent :lexical t
|
||
:END:
|
||
The early init file is the file loaded before anything else in
|
||
Emacs. This is where I put some options in order to disable as quickly
|
||
as possible some built-in features of Emacs before they can be even
|
||
loaded, speeding Emacs up a bit.
|
||
#+begin_src emacs-lisp :mkdirp yes
|
||
(setq package-enable-at-startup nil
|
||
inhibit-startup-message t
|
||
frame-resize-pixelwise t ; fine resize
|
||
package-native-compile t) ; native compile packages
|
||
(scroll-bar-mode -1) ; disable scrollbar
|
||
(tool-bar-mode -1) ; disable toolbar
|
||
(tooltip-mode -1) ; disable tooltips
|
||
(set-fringe-mode 10) ; give some breathing room
|
||
(menu-bar-mode -1) ; disable menubar
|
||
(blink-cursor-mode 0) ; disable blinking cursor
|
||
#+end_src
|
||
|
||
*** Better garbage collection
|
||
Emacs sometimes freezes up a bit when doing some garbage collection,
|
||
which is not super. So, in order to fix that, I do two things:
|
||
1. Set up a really high threshold (I have a lot of RAM to spare, so I
|
||
don’t really care),
|
||
2. Hook garbage collection thirty seconds after Emacs loses focus and
|
||
every thirty seconds after that,
|
||
3. Cancel garbage collection if Emacs gains focus back within this
|
||
thirty seconds window.
|
||
|
||
With a garbage collection threshold, Emacs should never garbage
|
||
collect on its own, and Emacs is free to freeze up for a few seconds
|
||
when it loses focus since I’m looking away.
|
||
#+begin_src emacs-lisp
|
||
(setq garbage-collection-messages t ;; tell me when garbage collecting
|
||
gc-cons-threshold (* 16 1024 1024 1024)) ;; 16GiB of RAM
|
||
|
||
(defmacro my/time (&rest body)
|
||
`(let ((time (current-time)))
|
||
,@body
|
||
(float-time (time-since time))))
|
||
|
||
(defun my/garbage-collect ()
|
||
"Garbage collect and tell the user how much time it took."
|
||
(message "Garbage collector ran for %.06fs"
|
||
(my/time (garbage-collect))))
|
||
|
||
(defvar my/gc-timer nil
|
||
"Timer for garbage collection. See
|
||
`my/garbage-collect-on-focus-lost'.")
|
||
|
||
(defun my/garbage-collect-on-focus-lost ()
|
||
"Garbage collect when Emacs loses focus.
|
||
|
||
Garbage collection is only triggered thirty seconds after losing
|
||
focus, and only once."
|
||
(if (frame-focus-state
|
||
(cancel-timer my/gc-timer))
|
||
(setq my/gc-timer (run-with-idle-timer 30 nil #'my/garbage-collect))))
|
||
|
||
(add-function :after after-focus-change-function #'my/garbage-collect-on-focus-lost)
|
||
#+end_src
|
||
|
||
To write this, I mostly based myself on [[https://news.ycombinator.com/item?id=39190110][this HackerNews thread]] and [[https://akrl.sdf.org/#orgc15a10d][its
|
||
related article]].
|
||
|
||
** Emacs Behavior
|
||
*** Editing Text in Emacs
|
||
I *never* want to keep trailing spaces in my files, which is why I’m
|
||
doing this:
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'before-save-hook #'whitespace-cleanup)
|
||
#+end_src
|
||
|
||
I don’t understand why some people add two spaces behind a full stop,
|
||
I sure don’t. Let’s tell Emacs.
|
||
#+begin_src emacs-lisp
|
||
(setq-default sentence-end-double-space nil)
|
||
#+end_src
|
||
|
||
There is a minor mode in Emacs which allows having a finer way of
|
||
jumping from word to word: ~global-subword-mode~. It detects if what
|
||
Emacs usually considers a word can be understood as several words, as
|
||
in camelCase words, and allows us to jump words on this finer level.
|
||
#+begin_src emacs-lisp
|
||
(global-subword-mode 1)
|
||
#+end_src
|
||
|
||
Changing half my screen each time my cursor goes too high or too low
|
||
is not exactly ideal. Fortunately, if we set ~scroll-conservatively~
|
||
high enough we can have the cursor stay on top or at the bottom of the
|
||
screen while the text scrolls progressively.
|
||
#+begin_src emacs-lisp
|
||
(setq scroll-conservatively 1000)
|
||
#+end_src
|
||
|
||
Lastly, I want the default mode for Emacs to be Emacs Lisp.
|
||
#+begin_src emacs-lisp
|
||
(setq-default initial-major-mode 'emacs-lisp-mode)
|
||
#+end_src
|
||
|
||
**** Indentation
|
||
I don’t like tabs. They rarely look good, and if I need it I can
|
||
almost always tell Emacs to use them through a ~.dir-locals.el~ file or
|
||
through the config file of my code formatter. So by default, let’s
|
||
disable them:
|
||
#+begin_src emacs-lisp
|
||
(setq-default indent-tabs-mode nil)
|
||
(add-hook 'prog-mode-hook (lambda () (setq indent-tabs-mode nil)))
|
||
#+end_src
|
||
|
||
Just to go on a little tangent here: I don’t exactly /hate/ tabs, but I
|
||
find them really annoying when your text editor knows only them. Sure,
|
||
for indentation they work great, and they allow different people
|
||
getting different settings in their text editor depending on their
|
||
preferred tastes —some may prefer 2 spaces tabs, some may prefer 4
|
||
spaces tabs, some deranged people prefer 8 spaces tabs, and some
|
||
monsters prefer 3!
|
||
|
||
But the thing is, once you indented your code, and then you need
|
||
alignment, tabs don’t work any more! Or they may on *your* text editor
|
||
but not on your coworker’s! (He’s the one using 3 spaces tabs by the
|
||
way).
|
||
|
||
So, is the answer to use spaces instead of tabs, and screw peoples’
|
||
preferences in terms of tabs width? No, I say the answer is more
|
||
moderate than that, and it might frighten or anger some of you at
|
||
first: use both spaces and tabs. Now, before you lynch me on the main
|
||
avenue in front of everyone, let me tell you absolutely no one should
|
||
ever be mixing spaces and tabs for indentation, that would be
|
||
absolutely terrible and would bring the worst of both worlds. What’s
|
||
the best of both worlds then?
|
||
#+begin_center
|
||
/Tabs for indentation/
|
||
|
||
/Spaces for alignment/
|
||
#+end_center
|
||
|
||
I haven’t found a way to automate that in Emacs yet aside from
|
||
formatters’ config file, and tabs look bat in EmacsLisp anyway, so
|
||
I’ll stick with spaces by default and change it where needed.
|
||
|
||
*** GPG pinentry
|
||
I’d like Emacs to be responsible for decrypting my GPG encrypted files
|
||
when in Emacs. This can be done with the following line.
|
||
#+begin_src emacs-lisp
|
||
(setq epg-pinentry-mode 'loopback)
|
||
#+end_src
|
||
|
||
*** Programming Modes
|
||
First off, my definition of what makes a “programming mode” doesn’t exactly
|
||
fit mine, so on top of ~prog-mode~, let’s add a few other modes.
|
||
#+name: line-number-modes-table
|
||
| Modes |
|
||
|------------|
|
||
| prog-mode |
|
||
| latex-mode |
|
||
|
||
#+name: prog-modes-gen
|
||
#+header: :cache yes :exports none :tangle no
|
||
#+begin_src emacs-lisp :var modes=line-number-modes-table
|
||
(mapconcat (lambda (mode) (format "%s-hook" (car mode)))
|
||
modes
|
||
" ")
|
||
#+end_src
|
||
|
||
**** Line Number
|
||
Since version 26, Emacs has a built-in capacity of displaying line
|
||
numbers on the left-side of the buffer. This is a fantastic feature
|
||
that should actually be the default for all programming modes.
|
||
|
||
#+begin_src emacs-lisp
|
||
(dolist (mode '(<<prog-modes-gen()>>))
|
||
(add-hook mode #'display-line-numbers-mode))
|
||
#+end_src
|
||
|
||
**** Folding code
|
||
Most programming languages can usually have their code folded, be it
|
||
code between curly braces, chunks of comments or code on another level
|
||
of indentation (Python, why…?). The minor-mode that enables that is
|
||
~hs-minor-mode~, let’s enable it for all of these programming modes:
|
||
#+begin_src emacs-lisp
|
||
(dolist (mode '(<<prog-modes-gen()>>))
|
||
(add-hook mode #'hs-minor-mode))
|
||
#+end_src
|
||
|
||
*** Stay Clean, Emacs!
|
||
As nice as Emacs is, it isn’t very polite or clean by default: open a
|
||
file, and it will create backup files in the same directory. But then,
|
||
when you open your directory with your favourite file manager and see
|
||
almost all of your files duplicated with a =~= appended to the filename,
|
||
it looks really uncomfortable! This is why I prefer to tell Emacs to
|
||
keep its backup files to itself in a directory it only will access.
|
||
#+begin_src emacs-lisp
|
||
(setq backup-directory-alist `(("." . ,(expand-file-name ".tmp/backups/"
|
||
user-emacs-directory))))
|
||
#+end_src
|
||
|
||
When using LSP with Typescript projects, my =tsconfig.json= or its
|
||
equivalent would constantly get polluted with symlinks going through
|
||
my home directory, creating some horror imports. This is because the
|
||
server tries to import some files through the symlink of the backup of
|
||
the file I want to import. By forbidding Emacs to create symlinks for
|
||
backups, we should avoid this problem.
|
||
#+begin_src emacs-lisp
|
||
(setq backup-by-copying t)
|
||
#+end_src
|
||
|
||
It also loves to litter its ~init.el~ with custom variables here and
|
||
there, but the thing is: I regenerate my ~init.el~ each time I tangle
|
||
this file! How can I keep Emacs from adding stuff that will be almost
|
||
immediately lost? Did someone say /custom file/?
|
||
#+begin_src emacs-lisp
|
||
(setq-default custom-file (expand-file-name ".custom.el" user-emacs-directory))
|
||
(when (file-exists-p custom-file) ; Don’t forget to load it, we still need it
|
||
(load custom-file))
|
||
#+end_src
|
||
|
||
If we delete a file, we want it moved to the trash, not simply deleted.
|
||
#+begin_src emacs-lisp
|
||
(setq delete-by-moving-to-trash t)
|
||
#+end_src
|
||
|
||
Finally, the scratch buffer always has some message at its beginning, I
|
||
don’t want it!
|
||
#+begin_src emacs-lisp
|
||
(setq-default initial-scratch-message nil)
|
||
#+end_src
|
||
|
||
*** Stay Polite, Emacs!
|
||
When asking for our opinion on something, Emacs loves asking us to
|
||
answer by “yes” or “no”, but *in full*! That’s very rude! Fortunately,
|
||
we can fix this. Note that the configuration changed in Emacs 29.
|
||
#+begin_src emacs-lisp
|
||
(if (version<= emacs-version "28")
|
||
(defalias 'yes-or-no-p 'y-or-n-p)
|
||
(setopt use-short-answers t))
|
||
#+end_src
|
||
|
||
This will make Emacs ask us for either hitting the ~y~ key for “yes”, or
|
||
the ~n~ key for “no”. Much more polite!
|
||
|
||
It is also very impolite to keep a certain version of a file in its
|
||
buffer when said file has changed on disk. Let’s change this
|
||
behaviour:
|
||
#+begin_src emacs-lisp
|
||
(global-auto-revert-mode 1)
|
||
#+end_src
|
||
|
||
Much more polite! Note that if the buffer is modified and its changes
|
||
haven’t been saved, it will not automatically revert the buffer and
|
||
your unsaved changes won’t be lost. Very polite!
|
||
|
||
*** Misc
|
||
Let’s raise Emacs undo memory to 10 MB, and make Emacs auto-save our
|
||
files by default.
|
||
#+begin_src emacs-lisp
|
||
(setq undo-limit 100000000
|
||
auto-save-default t)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq window-combination-resize t) ; take new window space from all other windows
|
||
#+end_src
|
||
|
||
** Personal Information
|
||
Emacs needs to know its master! For various reasons by the way, some
|
||
packages rely on these variables to know who it is talking to or
|
||
dealing with, such as ~mu4e~ which will guess who you are if you haven’t
|
||
set it up correctly.
|
||
#+begin_src emacs-lisp
|
||
(setq user-full-name "Lucien Cartier-Tilet"
|
||
user-real-login-name "Lucien Cartier-Tilet"
|
||
user-login-name "phundrak"
|
||
user-mail-address "lucien@phundrak.com")
|
||
#+end_src
|
||
|
||
** Visual Configuration
|
||
The first visual setting in this section will activate the visible
|
||
bell. What it does is I get a visual feedback each time I do something
|
||
Emacs doesn’t agree with, like trying to go up a line when I’m already
|
||
at the top of the buffer.
|
||
#+begin_src emacs-lisp
|
||
(setq visible-bell t)
|
||
#+end_src
|
||
|
||
It is nicer to see a cursor cover the actual space of a character.
|
||
#+begin_src emacs-lisp
|
||
(setq x-stretch-cursor t)
|
||
#+end_src
|
||
|
||
When text is ellipsed, I want the ellipsis marker to be a single
|
||
character of three dots. Let’s make it so:
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'mule-util
|
||
(setq truncate-string-ellipsis "…"))
|
||
#+end_src
|
||
|
||
With Emacs 29.0.50 onwards, a new frame parameter exists:
|
||
~alpha-background~. Unlike ~alpha~, this frame parameter only makes Emacs’
|
||
background transparent, excluding images and text.
|
||
#+begin_src emacs-lisp
|
||
(add-to-list 'default-frame-alist '(alpha-background . 0.9))
|
||
#+end_src
|
||
|
||
*** Modeline Modules
|
||
I sometimes use Emacs in fullscreen, meaning my usual taskbar will be
|
||
hidden. This is why I want the current date and time to be displayed,
|
||
in an ISO-8601 style, although not exactly ISO-8601 (this is the best
|
||
time format, fight me).
|
||
#+begin_src emacs-lisp
|
||
(require 'time)
|
||
(setq display-time-format "%Y-%m-%d %H:%M")
|
||
(display-time-mode 1) ; display time in modeline
|
||
#+end_src
|
||
|
||
Something my taskbar doesn’t have is a battery indicator. However, I
|
||
want it enabled only if I am on a laptop or if a battery is available.
|
||
#+begin_src emacs-lisp
|
||
(let ((battery-str (battery)))
|
||
(unless (or (equal "Battery status not available" battery-str)
|
||
(string-match-p (regexp-quote "N/A") battery-str))
|
||
(display-battery-mode 1)))
|
||
#+end_src
|
||
|
||
This isn’t a modeline module per se, but we have an indicator of the
|
||
current line in Emacs. And although it is useful, I also often wish to
|
||
know which column I’m on. This can be activated like so:
|
||
#+begin_src emacs-lisp
|
||
(column-number-mode)
|
||
#+end_src
|
||
|
||
The following code is, as will several chunks of code in this config,
|
||
borrowed from [[https://tecosaur.github.io/emacs-config/#theme-modeline][TEC’s configuration]]. It hides the encoding information
|
||
of the file if the file itself is a regular UTF-8 file with ~\n~ line
|
||
ending. Be aware the ~doom-modeline-buffer-encoding~ variable is usabel
|
||
here only because I use the Doom modeline as seen below.
|
||
#+begin_src emacs-lisp
|
||
(defun modeline-contitional-buffer-encoding ()
|
||
"Hide \"LF UTF-8\" in modeline.
|
||
|
||
It is expected of files to be encoded with LF UTF-8, so only show
|
||
the encoding in the modeline if the encoding is worth notifying
|
||
the user."
|
||
(setq-local doom-modeline-buffer-encoding
|
||
(unless (and (memq (plist-get (coding-system-plist buffer-file-coding-system) :category)
|
||
'(coding-category-undecided coding-category-utf-8))
|
||
(not (memq (coding-system-eol-type buffer-file-coding-system) '(1 2))))
|
||
t)))
|
||
#+end_src
|
||
|
||
Now, let’s automate the call to this function in order to apply the
|
||
modifications to the modeline each time we open a new file.
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'after-change-major-mode-hook #'modeline-contitional-buffer-encoding)
|
||
#+end_src
|
||
|
||
*** Fonts
|
||
I don’t like the default font I usually have on my machines, I really
|
||
don’t. I prefer [[https://github.com/microsoft/cascadia-code][Cascadia Code]], as it also somewhat supports the [[https://www.internationalphoneticassociation.org/][IPA]].
|
||
#+begin_src emacs-lisp
|
||
(defvar phundrak/default-font-size 90
|
||
"Default font size.")
|
||
|
||
(defvar phundrak/default-font-name "Cascadia Code"
|
||
"Default font.")
|
||
|
||
(defun my/set-font ()
|
||
(when (find-font (font-spec :name phundrak/default-font-name))
|
||
(set-face-attribute 'default nil
|
||
:font phundrak/default-font-name
|
||
:height phundrak/default-font-size)))
|
||
|
||
(my/set-font)
|
||
(add-hook 'server-after-make-frame-hook #'my/set-font)
|
||
#+end_src
|
||
|
||
*** Frame Title
|
||
This is straight-up copied from [[https://tecosaur.github.io/emacs-config/config.html#window-title][TEC]]’s configuration. See their comment
|
||
on the matter.
|
||
#+begin_src emacs-lisp
|
||
(setq frame-title-format
|
||
'(""
|
||
"%b"
|
||
(:eval
|
||
(let ((project-name (projectile-project-name)))
|
||
(unless (string= "-" project-name)
|
||
(format (if (buffer-modified-p) " ◉ %s" " ● %s - Emacs") project-name))))))
|
||
#+end_src
|
||
|
||
** A better custom variable setter
|
||
Something people often forget about custom variables in Elisp is they
|
||
can have a custom setter that will run some code if we set the
|
||
variable properly with ~customize-set-variable~, so ~setq~ shouldn’t be
|
||
the user’s choice by default. But repeatedly writing
|
||
~customize-set-variable~ can get tiring and boring. So why not take the
|
||
best of both world and create ~csetq~, a ~setq~ that uses
|
||
~customize-set-variable~ under the hood while it keeps a syntax similar
|
||
to the one ~setq~ uses?
|
||
#+begin_src emacs-lisp
|
||
(defmacro csetq (&rest forms)
|
||
"Bind each custom variable FORM to the value of its VAL.
|
||
|
||
FORMS is a list of pairs of values [FORM VAL].
|
||
`customize-set-variable' is called sequentially on each pair
|
||
contained in FORMS. This means `csetq' has a similar behaviour as
|
||
`setq': each VAL expression is evaluated sequentially, i.e. the
|
||
first VAL is evaluated before the second, and so on. This means
|
||
the value of the first FORM can be used to set the second FORM.
|
||
|
||
The return value of `csetq' is the value of the last VAL.
|
||
|
||
\(fn [FORM VAL]...)"
|
||
(declare (debug (&rest sexp form))
|
||
(indent 1))
|
||
;; Check if we have an even number of arguments
|
||
(when (= (mod (length forms) 2) 1)
|
||
(signal 'wrong-number-of-arguments (list 'csetq (1+ (length forms)))))
|
||
;; Transform FORMS into a list of pairs (FORM . VALUE)
|
||
(let (sexps)
|
||
(while forms
|
||
(let ((form (pop forms))
|
||
(value (pop forms)))
|
||
(push `(customize-set-variable ',form ,value)
|
||
sexps)))
|
||
`(progn ,@(nreverse sexps))))
|
||
#+end_src
|
||
|
||
I first got inspired by [[https://oremacs.com/2015/01/17/setting-up-ediff/][this blog article]] (archived article, just in
|
||
case) but it seems the code snippet no longer works properly, so not
|
||
only did I have to modify it to make it work with an arbitrary amount
|
||
of arguments (as long as it’s pairs of variables and their value), but
|
||
I also had to make the code simply work.
|