Also precompile user-lisp content if the ELC files are older than their original source file.
158 lines
6.5 KiB
Org Mode
158 lines
6.5 KiB
Org Mode
#+title: Emacs Configuration
|
||
#+setupfile: ../headers
|
||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/init.el
|
||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||
|
||
* Emacs Configuration
|
||
|
||
[[file:./img/emacs.svg]]
|
||
|
||
** Introduction
|
||
After a couple of years using Spacemacs and a failed attempt at
|
||
switching to DoomEmacs, I’m finally switching back to a vanilla
|
||
configuration! Why? Because I got tired of the framework getting in my
|
||
way when I wanted to do stuff. I’m sure this is more applicable to
|
||
Spacemacs than DoomEmacs since the latter has nice macros written to
|
||
easily add new packages and configure them, such as ~package!~, ~after!~,
|
||
and others. But ultimately, I wanted to have a system I designed
|
||
entirely, with the keybinds I want, the packages I want.
|
||
|
||
Also, why Emacs? You know this famous quote:
|
||
#+begin_quote
|
||
Emacs is a great operating system, it just lacks a good text editor.
|
||
#+end_quote
|
||
|
||
It’s actually pretty true in my opinion. Emacs is basically a Lisp
|
||
machine with a default text editor, programmed with EmacsLisp, a
|
||
general-purpose programming language. Therefore, if you want to do
|
||
something in Emacs, with enough Elisp you can do it --- if it’s not in
|
||
Emacs already, that is.
|
||
|
||
#+attr_html: :alt Dammit Emacs… :loading lazy
|
||
#+caption: [[https://xkcd.com/378/][XKCD n°378]]: Real Programmers
|
||
[[file:./img/real_programmers.png]]
|
||
|
||
** A Warning Before You Proceed
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
This configuration makes heavy use of the [[https://orgmode.org/manual/Noweb-Reference-Syntax.html][noweb]] syntax. This means if
|
||
you encounter some code that looks ~<<like-this>>~, org-mode will
|
||
replace this snippet with another code snippet declared elsewhere in
|
||
my configuration. If you see some code that looks ~<<like-this()>>~,
|
||
some generating code will run and replace this piece of text with the
|
||
text generated. A quick example:
|
||
#+begin_src elisp
|
||
(defun hello ()
|
||
<<generate-docstring()>>
|
||
<<print-hello>>)
|
||
#+end_src
|
||
|
||
Will instead appear as
|
||
#+begin_src emacs-lisp :noweb yes
|
||
(defun hello ()
|
||
<<generate-docstring()>>
|
||
<<print-hello>>)
|
||
#+end_src
|
||
|
||
This is because I have the block of code below named
|
||
~generate-docstring~ which generates an output, which replaces its noweb
|
||
tag. You can recognize noweb snippets generating code with the
|
||
parenthesis. Often, such blocks aren’t visible in my HTML exports, but
|
||
you can still see them if you open the actual org source file.
|
||
#+name: generate-docstring
|
||
#+begin_src emacs-lisp
|
||
(concat "\""
|
||
"Print \\\"Hello World!\\\" in the minibuffer."
|
||
"\"")
|
||
#+end_src
|
||
|
||
On the other hand, noweb snippets without parenthesis simply replace
|
||
the snippet with the equivalent named code block. For instance the one
|
||
below is named ~print-hello~ and is placed as-is in the target source
|
||
block.
|
||
#+name: print-hello
|
||
#+begin_src emacs-lisp
|
||
(message "Hello World!")
|
||
#+end_src
|
||
|
||
** Loading All Configuration Modules
|
||
Here are all my Emacs configuration modules, each dedicated to a
|
||
specific aspect of my Emacs configuration.
|
||
|
||
#+name: emacs-modules
|
||
| Module Name | Config Page |
|
||
|------------------------+----------------------------------|
|
||
| =basic-config.el= | [[file:./basic-config.org][Basic Configuration]] |
|
||
| =custom-elisp.el= | [[file:./custom-elisp.org][Custom Elisp]] |
|
||
| =package-manager.el= | [[file:./package-manager.org][Package Manager]] |
|
||
| =keybinding-managers.el= | [[file:keybinding-managers.org][Keybinding Managers]] |
|
||
| =applications.el= | [[file:./packages/applications.org][Packages — Applications]] |
|
||
| =autocompletion.el= | [[file:./packages/autocompletion.org][Packages — Autocompletion]] |
|
||
| =editing.el= | [[file:./packages/editing.org][Packages — Editing]] |
|
||
| =emacs-builtin.el= | [[file:./packages/emacs-builtin.org][Packages — Emacs Built-ins]] |
|
||
| =helpful.el= | [[file:./packages/helpful.org][Packages — Making My Life Easier]] |
|
||
| =latex.el= | [[file:./packages/latex.org][Packages — LaTeX]] |
|
||
| =misc.el= | [[file:./packages/misc.org][Packages — Misc]] |
|
||
| =org.el= | [[file:./packages/org.org][Packages — Org Mode]] |
|
||
| =programming.el= | [[file:./packages/programming.org][Packages — Programming]] |
|
||
| =visual-config.el= | [[file:./packages/visual-config.org][Packages — Visual Configuration]] |
|
||
| =keybindings.el= | [[file:./keybindings.org][Keybindings]] |
|
||
|
||
#+name: modules
|
||
#+begin_src emacs-lisp :tangle no :cache yes :var modules=emacs-modules :exports none :results list
|
||
(mapcar (lambda (module) (string-trim (car module) "=" "="))
|
||
modules)
|
||
#+end_src
|
||
|
||
#+RESULTS[1c6034eb268577137e95a6f828c249fc306943b1]: modules
|
||
- basic-config.el
|
||
- custom-elisp.el
|
||
- package-manager.el
|
||
- keybinding-managers.el
|
||
- applications.el
|
||
- autocompletion.el
|
||
- editing.el
|
||
- emacs-builtin.el
|
||
- helpful.el
|
||
- latex.el
|
||
- misc.el
|
||
- org.el
|
||
- programming.el
|
||
- visual-config.el
|
||
- keybindings.el
|
||
|
||
The first thing Emacs does is verify if any of these modules need to
|
||
be byte-compiled again, i.e. if the =.el= file is younger than its
|
||
corresponding =.elc= file. If so, the =.el= file is byte-compiled.
|
||
|
||
#+begin_src emacs-lisp :var modules=modules :results none
|
||
(defun my/mtime (file)
|
||
"Get the modification time of FILE as a timestamp."
|
||
(let* ((current-time-list nil)
|
||
(attr (file-attributes file))
|
||
(time-attr (file-attribute-modification-time attr)))
|
||
(/ (car time-attr) (cdr time-attr))))
|
||
|
||
;; `modules' is a variable created by org-mode from the table of modules
|
||
(let* ((config-dir (expand-file-name "user-lisp" user-emacs-directory))
|
||
(modules (mapcar (lambda (module)
|
||
(file-name-sans-extension (expand-file-name module config-dir)))
|
||
modules)))
|
||
(dolist (module modules)
|
||
(let* ((el-file (concat module ".el"))
|
||
(elc-file (concat module ".elc"))
|
||
(el-last-change (my/mtime el-file))
|
||
(elc-last-change (if (file-exists-p elc-file) (my/mtime elc-file) 0)))
|
||
(when (> el-last-change elc-last-change)
|
||
(byte-compile-file el-file)))))
|
||
#+end_src
|
||
|
||
Now we can load them.
|
||
#+begin_src emacs-lisp :var modules=modules :results none
|
||
(dolist (module (mapcar #'file-name-sans-extension modules))
|
||
(load (expand-file-name module
|
||
(expand-file-name "user-lisp" user-emacs-directory))))
|
||
#+end_src
|