#+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)
      (when (timerp my/gc-timer)
       (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.
