378 lines
15 KiB
Org Mode
378 lines
15 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
|
|||
|
(setq gc-cons-threshold (* 1024 1024 1024))
|
|||
|
#+end_src
|
|||
|
|
|||
|
** 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 to have 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 anymore! 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.
|
|||
|
|
|||
|
*** 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 favorite 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
|
|||
|
|
|||
|
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 scatch 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.
|
|||
|
#+begin_src emacs-lisp
|
|||
|
(defalias 'yes-or-no-p 'y-or-n-p)
|
|||
|
#+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 behavior:
|
|||
|
#+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 tring 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 pairs
|
|||
|
contained in FORMS. This means `csetq' has a similar behaviour as
|
|||
|
`setq': each VAL expression are 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.
|