emacs.d/README.org

1149 lines
47 KiB
Org Mode
Raw Normal View History

2021-11-29 13:14:56 +01:00
#+startup: content
#+title: Emacs setup for use with LaTeX, Org, and Python
#+author: Gerard Vermeulen
#+babel: :cache no
#+property: header-args :tangle init.el :comments link
#+property: header-args:emacs-lisp :results silent
#+latex_class: article
#+latex_class_options: [11pt,a4paper,svgnames]
#+latex_header: \hypersetup{
#+latex_header: citecolor=blue,
#+latex_header: colorlinks=true,
#+latex_header: filecolor=blue,
#+latex_header: hyperfootnotes=false,
#+latex_header: linkcolor=blue,
#+latex_header: unicode=true,
#+latex_header: urlcolor=blue
#+latex_header: }
#+latex_header: \usepackage{minted}
#+latex_header: \usemintedstyle{xcode}
#+latex_header: \usepackage[
#+latex_header: headheight=20mm,
#+latex_header: top=40mm,
#+latex_header: bottom=20mm,
#+latex_header: left=0.1\paperwidth,
#+latex_header: right=0.1\paperwidth,
#+latex_header: heightrounded,
#+latex_header: verbose,
#+latex_header: ]{geometry}
* Introduction
:PROPERTIES:
:CUSTOM_ID: sec:introduction
:END:
This Emacs setup aims to install automatically a minimal set of extension
packages that allows to handle my reports and presentations. The file format of
the reports is [[https://orgmode.org/][Org Mode]] plain text with [[https://www.python.org/][Python]] source code blocks and the file
format of the presentations is [[https://www.latex-project.org/][LaTeX]].
This [[info:org#Top][org]] file (more precisely the original [[info:org#Top][org]] source file of this file)
illustrates my work-flow by showing:
1. How tangle (or export) source blocks from [[info:org#Top][org]] files. This file contains the
files =early-init.el=, =init.el=, =latexmkrc=, =org-store-link=, and
=example.py= for tangling.
2. How to export [[info:org#Top][org]] files to other formats such as [[https://en.wikipedia.org/wiki/HTML][HTML]], [[https://www.latex-project.org/][LaTeX]], and [[https://en.wikipedia.org/wiki/PDF][PDF]].
3. How [[info:org#Hyperlinks][org hyperlinks (info)]] allow to link inside and outside [[info:org#Top][Org Mode]]: hover
over or click on the links to experiment.
The [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX - Aalborg University Center TeX]] extension package provides a
powerful [[https://en.wikipedia.org/wiki/Text-based_user_interface][Text-based User Interface (TUI)]] environment to edit the [[https://www.latex-project.org/][LaTeX]]
presentations.
The [[https://github.com/bdarcus/citar][citar]] extension package provides quick filtering and selecting of
bibliographic entries, and the option to run different commands on those
selections. [[https://github.com/bdarcus/citar][Citar]] requires [[info:org#Top][Org-9.5 (info)]], which is already part of Emacs-28.1.
[[https://github.com/bdarcus/citar][Citar]] exploits the enhancements of Emacs' builtin selection mechanism provided
by the extension packages [[https://github.com/minad/vertico][vertico]], [[https://github.com/oantolin/orderless][orderless]], [[https://github.com/oantolin/embark][embark]], [[https://github.com/minad/marginalia][marginalia]], and [[https://github.com/minad/consult][consult]].
The [[https://github.com/andras-simonyi/citeproc-el][citeproc]] extension package provides [[https://citationstyles.org/][CSL: citation style language]] processing
capabilities to [[https://github.com/bdarcus/citar][citar]] and [[https://orgmode.org/][Org Mode]].
The [[https://github.com/vedang/pdf-tools][pdf-tools]] extension package renders [[https://en.wikipedia.org/wiki/PDF][PDF]] file with the possibility to
annotate the file or to click on anchors in the file that link back to the
original [[https://www.latex-project.org/][LaTeX]] file of a document. An example of my work-flow are the steps how
to convert this [[info:org#Top][org]] file to [[https://en.wikipedia.org/wiki/PDF][PDF]] and to see the result with [[https://github.com/vedang/pdf-tools][pdf-tools]] in Emacs:
execute the commands ~pdf-tools-install~, ~org-babel-tangle~,
~org-latex-export-latex-to-latex~, and ~compile~. This sets up an infinite
[[https://www.latex-project.org/][LaTeX]] compilation loop to update and redisplay the [[https://en.wikipedia.org/wiki/PDF][PDF]] file after excution of
the ~org-latex-export-latex-to-latex~ command in this buffer.
[[info:emacs#Top][Emac (info)]]
Here follows a list of interesting Emacs configurations:
1. [[https://github.com/oantolin/emacs-config][Omar Antolín Camarena's configuration]]
2. [[https://gitlab.com/ambrevar/dotfiles][Pierre Neirhardt's configuration]] implements lazy loading without help of
external packages. I have stolen his approach of using lazy loading to
silently ignore the setup stanzas of uninstalled extension packages.
3. [[https://sachachua.com/dotemacs/][Sacha Chua's configuration]] is an example of producing the Emacs
initialization files by tangling an [[info:org#Top][org]] file. It gives me the impression
that she is a very practical person trying to achieve her goals by the most
efficient means. I have stolen her idea of using [[https://github.com/quelpa/quelpa][quelpa]] to install packages
from any source.
4. [[https://github.com/purcell/emacs.d][Steve Purcell's configuration]] is well organized and a show-case of readable
[[info:elisp#Top][Emacs lisp (info)]] code. I have stolen his idea of versioning the
~package-user-dir~ variable to prevent clashes between the byte-compiler
output of different Emacs versions.
5. [[https://github.com/tecosaur/emacs-config][Timothy E. Chapman]]
* [[info:emacs#Early Init File][Early Init File (info)]]
:PROPERTIES:
:CUSTOM_ID: sec:early-init-file
:END:
The err
[[#sec:package-bootstrapping][Package Bootstrapping]]
qLF FF
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp :tangle early-init.el
;;; early-init.el --- user early-init file -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:
(setq package-enable-at-startup nil)
(setq-default load-prefer-newer t)
(provide 'early-init)
;; Emacs looks for "Local variables:" after the last "C-q C-j C-q C-l".
;; Local Variables:
;; indent-tabs-mode: nil
;; End:
;;; earl-init.el ends here
#+end_src
* [[info:emacs#Init File][Init File (info)]] header
:PROPERTIES:
:CUSTOM_ID: sec:init-file-header
:END:
The [[info:elisp#Quoting][quoting (info)]] and the [[info:elisp#Backquote][backquote (info)]] pages explain how to understand the
reader macros ~'~ (quote), ~`~ (backquote), ~,~ (substitute) and ~@,~ (splice)
in the ~custom-set-variable~ function call below.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
;;; init.el --- user init file -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:
(require 'cl-lib)
;; https://with-emacs.com/posts/tutorials/almost-all-you-need-to-know-about-variables/
(defmacro csetq (sym val)
`(funcall (or (get ',sym 'custom-set) 'set-default) ',sym ,val))
(custom-set-variables
'(after-save-hook #'executable-make-buffer-file-executable-if-script-p)
'(column-number-mode t)
'(cursor-type 'box)
`(custom-file
,(locate-user-emacs-file
(format "custom-%s.%s.el" emacs-major-version emacs-minor-version)))
'(epg-pinentry-mode 'loopback)
'(global-hl-line-mode t)
'(global-hl-line-sticky-flag t)
'(history-delete-duplicates t)
'(history-length 500)
'(indent-tabs-mode nil)
'(inhibit-startup-buffer-menu t)
'(inhibit-startup-screen t)
'(initial-buffer-choice t)
'(initial-scratch-message "")
`(insert-directory-program ,(or (executable-find "gls")
(executable-find "ls")))
'(kill-ring-max 300)
'(package-archive-priorities '(("gnu" . 1)
("nongnu" . 1)))
'(package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
`(package-user-dir
,(locate-user-emacs-file
(format "elpa-%s.%s" emacs-major-version emacs-minor-version)))
'(recentf-mode t)
'(save-place-mode t)
'(savehist-additional-variables
'(eww-history
kill-ring
regexp-search-string
search-ring
search-string))
'(savehist-mode t)
'(savehist-save-minibuffer-history 1)
'(scroll-bar-mode nil)
'(tab-always-indent 'complete)
'(tab-width 8)
'(tool-bar-mode nil)
'(url-cookie-trusted-urls nil)
'(url-cookie-untrusted-urls '(".*"))
'(use-dialog-box nil)
'(use-short-answer t)
'(view-read-only t))
(when (eq system-type 'darwin)
(custom-set-variables
'(ns-alternate-modifier nil)
'(ns-command-modifier 'meta)
'(ns-right-command-modifier 'super)))
(when (eq window-system 'ns)
(add-to-list 'initial-frame-alist '(height . 51))
(add-to-list 'initial-frame-alist '(width . 180)))
#+end_src
* Package bootstrapping
:PROPERTIES:
:CUSTOM_ID: sec:package-bootstrapping
:END:
[[info:emacs#Package Installation][Emacs installs packages]] from archives on the internet.
This setup uses three archives in two decreasing levels of priority:
1. The [[https://elpa.gnu.org/][GNU Emacs Lisp Package Archive]] or the [[https://elpa.nongnu.org/][NonGNU Emacs Lisp Package Archive]].
3. The [[https://melpa.org/#/][MELPA - Milkypostmans Emacs Lisp Package Archive]].
Finally, the [[https://github.com/quelpa/quelpa][quelpa]] tool allows to fetch code from any source and build a
package on your computer before installation. It allows to install a package
from [[https://melpa.org/#/][MELPA]] instead of [[https://elpa.gnu.org/][GNU ELPA]] or [[https://elpa.nongnu.org/][NonGNU ELPA]], breaking the priority order.
The output of the byte-compiler may change with each new Emacs release.
Therefore, in order to prevent collisions between different Emacs versions, the
package-user-directory has a suffix containing the major- and minor-version
numbers of Emacs.
The order of the next 1nd, 2nd, and 3rd package-bootstrapping blocks matters
because each of those blocks prepares Emacs for the next block.
If present, the package [[https://github.com/emacscollective/no-littering][no-littering]] helps to keep =~/.emacs.d= clean.
The code assumes that the package system is in a *virgin* state in case the
package [[https://github.com/emacscollective/no-littering][no-littering]] is not present. Refreshing the contents of available
packages at least once is a requirement in order to be able to install and load
any packages, hence also [[https://github.com/emacscollective/no-littering][no-littering]].
You have to refresh the list of available packages yourself before updating
the installed packages.
Finally, ~my-install-packages~ ensures installation of all packages in a list of
packages.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
;; The is the 1st package bootstrapping block.
(require 'package)
(package-initialize)
(unless (require 'no-littering nil 'noerror)
(package-refresh-contents)
(package-install 'no-littering)
(require 'no-littering))
(defun my-install-packages (packages)
"Ensure installation of all packages in PACKAGES."
(dolist (package packages)
(unless (package-installed-p package)
(package-install package))))
#+end_src
Install the basic packages and in case this is Emacs-27.2, upgrade [[https://orgmode.org/][Org Mode]] for
compatibility with Emacs-28.1. The [[info:elisp#Backquote][info:backquote]] page explains how to read the
~`~ and ~,@~ in the definition of ~my-packages~ variable below.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
;; The is the 2nd package bootstrapping block.
(defvar my-packages
`(
,@(when (version< emacs-version "28.0")
'(org)) ; plain text thought organizer
auctex ; Aalborg University Center TeX
blacken ; Black Python-code formatter client
citar ; bibliography handling
citeproc ; bibliography handling
consult ; consult completing-read
;; eglot ; Emacs polyGLOT LSP client
elpy ; Python development environment
embark ; act on any buffer selection
htmlize ; convert buffer contents to HTML
leuven-theme ; beautiful color theme
;; lsp-mode ; Language Server Protocol client
marginalia ; minibuffer margin notes
orderless ; Emacs completion style
pdf-tools ; interactive docview replacement
pyenv-mode ; Python environment selector
quelpa ; install Emacs packages from source
vertico) ; VERTical Interactive Completion
"List of packages required packages.")
(my-install-packages my-packages)
#+end_src
Install the following packages with [[https://github.com/quelpa/quelpa][quelpa]]:
1. ~lisp-ui~ (including ~lisp-mode~), because ~lisp-ui~ is yet available from
[[https://elpa.gnu.org/][GNU ELPA]], [[https://elpa.nongnu.org/][NonGNU ELPA]], or [[https://melpa.org/#/][MELPA]].
2. ~eglot~ to get the latest version from [[https://melpa.org/#/][MELPA]].
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
;; This is the 3rd package bootstrapping block.
(when (package-installed-p 'quelpa)
(defun my-install-sources (sources)
(mapc (lambda (source)
(unless (package-installed-p (car source))
(quelpa source)))
sources))
(my-install-sources
'((lsp-ui :fetcher github :repo "emacs-lsp/lsp-ui")
(eglot :fetcher github :repo "joaotavora/eglot"))))
#+end_src
Install the optional packages.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
;; The is the 4th package bootstrapping block.
(defvar my-optional-packages
`(
,@(when (version< emacs-version "28.0")
'(modus-themes)) ; high foreground/background contrast themes
async ; asynchroneous processing
company ; complete anything
electric-operator ; automatic spacing around operators
elfeed ; web feed reader
emms ; Emacs Multi-Media System
iedit ; simultaneous multi-entity editing
laas ; LaTeX Auto-Activating Snippets
magit ; Git Text-based User Interface
nov ; EPUB reader
smartparens ; smart editing of character pairs
wgrep ; open a writable grep buffer
which-key ; on the fly key-binding help
wordnut ; WordNet lexical database
writegood-mode ; bullshit and weasel-word detector
ws-butler ; remove trailing whitespace
xr ; undo rx to grok regular expressions
yasnippet)) ; code or text template expansion
(defun my-install-optional-packages ()
(interactive)
(my-install-packages my-optional-packages))
#+end_src
* [[info:emacs#Emacs Server][Emacs Server (info)]]
Emacs can act as a server that listens to a socket to share its state (for
instance buffers and command history) with other programs by means of a shell
command =emacsclient=.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(when window-system
(unless (or noninteractive (daemonp))
(add-hook 'after-init-hook #'server-start)))
#+end_src
** [[https://www.personal.psu.edu/jcc8/latexmk/][Usage: latexmk compilation loop]]
:PROPERTIES:
:CUSTOM_ID: sec:latexmk-compilation-loop
:END:
The =latexmk= resource file in the next source code block shows how to use
=emacsclient= to (re)display the PDF file in Emacs after each succesful
(re)compilation on condition that the settings of the ~compile-command~ local
variable in section [[#sec:compile-with-latexmk][compile with latexmk]] are compatible.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src perl :tangle latexmkrc
# pdf previewer and update pdf previewer
$pdf_previewer = "emacsclient -e '(find-file-other-window %S)'";
$pdf_update_method = 4; # 4 runs a command to force the update
$pdf_update_command = "emacsclient -e '(with-current-buffer (find-buffer-visiting %S) (pdf-view-revert-buffer nil t))'";
# see for instance glossary.latexmkrc
add_cus_dep( 'acn', 'acr', 0, 'makeglossaries' );
add_cus_dep( 'glo', 'gls', 0, 'makeglossaries' );
$clean_ext .= " acr acn alg bbl glo gls glg run.xml";
sub makeglossaries {
my ($name, $path) = fileparse( $$Psource );
return system "makeglossaries -d '$path' '$name'";
}
# Emacs looks for "Local variables:" after the last "C-q C-j C-q C-l".
# Local Variables:
# mode: perl
# End:
#+end_src
** [[https://qutebrowser.org/doc/userscripts.html][Usage: qutebrowser userscript]]
The next block contains an userscript that sends a [[info:org#The store-link protocol][store-link org-protocol]]
message with the url and the title from [[https://qutebrowser.org][qutebrowser]] to =emacsclient=. The
function =urlencode= translates the url and the title for the message. The
[[info:python#Examples<22>][Python urllib examples]] show how to use =urlencode=. The final =execvp= call
deals with a [[https://qutebrowser.org][qutebrowser]] userscript requirement: the =emacsclient= process must
get the PID of the userscript that must kill itself after the take-over.
Termination of the =emacsclient= process hands control back to [[https://qutebrowser.org][qutebrowser]].
On a [[https://en.wikipedia.org/wiki/POSIX][POSIX]] system, you can run the userscript from [[https://qutebrowser.org][qutebrowser]] or from a
terminal to see whether it works. In case you try to run it from Emacs, Emacs
may hang or die.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+header: :comments none
#+header: :tangle-mode (identity #o755)
#+begin_src python :noeval :tangle org-store-link
#!/usr/bin/env python
from urllib.parse import urlencode
from os import environ, execvp
url = environ.get("QUTE_URL", "https://orgmode.org")
title = environ.get("QUTE_TITLE", "Org Mode")
parameters = urlencode({"url": url, "title": title})
print(payload := f"org-protocol://store-link?{parameters}")
execvp("emacsclient", ("-n", payload))
#+end_src
* Completion
:PROPERTIES:
:CUSTOM_ID: sec:completion
:END:
[[info:vertico#Top][Vertico (info)]] provides a performant and minimalistic vertical completion UI
based on the default completion system and behaves therefore correctly under all
circumstances. Vertico integrates well with fully supported complementary
packages to enrich the completion UI:
1. [[info:marginalia#Top][Marginalia (info)]] for rich annotations in the minibuffer.
2. [[info:consult#Top][Consult (info)]] for useful search and navigation commands.
3. [[info:embark#Top][Embark (info)]] for minibuffer actions with context menus.
4. [[info:orderless#Top][Orderless (info)]] for an advanced completion style.
[[https://cestlaz.github.io/post/using-emacs-80-vertico/][Using Vertico, Marginalia, Consult, and Embark]]
[[info:vertico#Configuration][Vertico configuration (link)]]
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(when (fboundp 'vertico-mode)
(vertico-mode +1)))
#+end_src
[[info:marginalia#Top][Marginalia (info)]] adds marginalia (margin annotations) to minibuffer
completions.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(when (fboundp 'marginalia-mode)
(marginalia-mode +1)))
#+end_src
Consult provides practical commands based on the Emacs completion
function.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(when (fboundp 'consult-apropos)
(custom-set-variables
'(consult-project-root-function #'vc-root-dir))
;; C-c bindings (mode-specific-map)
(global-set-key (kbd "C-c h") #'consult-history)
(global-set-key (kbd "C-c m") #'consult-mode-command)
;; C-x bindings (ctl-x-map)
(global-set-key (kbd "C-x M-:") #'consult-complex-command)
(global-set-key (kbd "C-x b") #'consult-buffer)
(global-set-key (kbd "C-x 4 b") #'consult-buffer-other-window)
(global-set-key (kbd "C-x 5 b") #'consult-buffer-other-frame)
(global-set-key (kbd "C-x r x") #'consult-register)
(global-set-key (kbd "C-x r b") #'consult-bookmark)
;; M-g bindings (goto-map)
(global-set-key (kbd "M-g g") #'consult-goto-line)
(global-set-key (kbd "M-g M-g") #'consult-goto-line)
(global-set-key (kbd "M-g o") #'consult-outline)
(global-set-key (kbd "M-g m") #'consult-mark)
(global-set-key (kbd "M-g k") #'consult-global-mark)
(global-set-key (kbd "M-g i") #'consult-imenu-project)
(global-set-key (kbd "M-g e") #'consult-error)
;; M-s bindings (search-map)
(global-set-key (kbd "M-s g") #'consult-git-grep)
(global-set-key (kbd "M-s f") #'consult-find)
(global-set-key (kbd "M-s k") #'consult-keep-lines)
(global-set-key (kbd "M-s l") #'consult-line)
(global-set-key (kbd "M-s m") #'consult-multi-occur)
(global-set-key (kbd "M-s u") #'consult-focus-lines)
;; Other bindings
(global-set-key (kbd "M-y") #'consult-yank-pop)
(global-set-key (kbd "<help> a") #'consult-apropos)
;; Tweak functions
(advice-add 'completing-read-multiple
:override #'consult-completing-read-multiple)
(fset 'multi-occur #'consult-multi-occur)))
#+end_src
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(when (cl-every #'fboundp '(embark-act embark-bindings embark-dwim))
(global-set-key (kbd "C-,") #'embark-act)
(global-set-key (kbd "C-:") #'embark-dwim)
(global-set-key (kbd "C-h B") #'embark-bindings)))
#+end_src
[[info:orderless#Top][Orderless (info)]]
#+begin_src emacs-lisp
(unless noninteractive
(when (fboundp 'orderless-filter)
(custom-set-variables
;; https://github.com/purcell/emacs.d/issues/778
'(completion-styles '(basic completion-partial orderless))
'(completion-category-defaults nil)
'(completion-category-overrides
'((file (styles partial-completion)))))
(add-hook 'minibuffer-setup-hook
(defun my-on-minibuffer-setup-hook()
(setq-default completion-styles '(substring orderless))))))
#+end_src
[[https://company-mode.github.io/][company-mode: modular in-buffer completion framework for Emacs]]
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(when (fboundp 'company-mode)
(custom-set-variables
;; https://github.com/purcell/emacs.d/issues/778
'(company-transformers '(company-sort-by-occurrence)))
(dolist (hook '(LaTeX-mode-hook
org-mode-hook
emacs-lisp-mode-hook
lisp-interaction-mode-hook
python-mode-hook
ielm-mode-hook))
(add-hook hook #'company-mode))))
#+end_src
* Reading
** EPUB files
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(when (fboundp 'nov-mode)
(add-to-list 'auto-mode-alist `(,(rx ".epub" eos) . nov-mode)))
#+end_src
** PDF files
The [[https://github.com/vedang/pdf-tools][pdf-tools]] package exploits the [[https://github.com/freedesktop/poppler][poppler]] library to render and to let you
annotate [[https://en.wikipedia.org/wiki/PDF][PDF]] files. It also exploits the [[https://wiki.contextgarden.net/SyncTeX][SyncTeX]] library to link anchors in [[https://en.wikipedia.org/wiki/PDF][PDF]]
files produced with LaTeX to the original LaTeX sources.
In order to use [[https://github.com/vedang/pdf-tools][pdf-tools]], you have to type =M-x pdf-tools-install= after
installation of [[https://github.com/vedang/pdf-tools][pdf-tools]] from [[https://melpa.org/][MELPA]] or after each update of [[https://github.com/freedesktop/poppler][poppler]] to build
or rebuild the =epdfinfo= executable that serves the [[https://en.wikipedia.org/wiki/PDF][PDF]] files to Emacs.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(when (fboundp 'pdf-tools-install)
(autoload 'pdf-view-mode "pdf-view")
(add-to-list 'magic-mode-alist '("%PDF" . pdf-view-mode)))
#+end_src
* Writing
** LaTeX
Loading =tex.el= immediately instead of lazily ensures proper initialization of
the [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]]. For instance, the ~TeX-master~ safe local variable in the =tex.el=
elisp library file has no autoload cookie. Without prior loading of =tex.el=,
Emacs will complain that ~TeX-master~ is no safe local variable in case it
reads a LaTeX file that sets ~TeX-master~.
Out of the box, [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] does not indent text between square brackets. The code
below corrects this by advising to override ~TeX-brace-count-line~ with
~my-TeX-brace-count-line~.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
;; Try to get the `safe-local-variable` predicate for `TeX-master`
(when (require 'tex nil 'noerror)
;; https://emacs.stackexchange.com/questions/17396/indentation-in-square-brackets
(defun my-TeX-brace-count-line ()
"Count number of open/closed braces."
(save-excursion
(let ((count 0) (limit (line-end-position)) char)
(while (progn
(skip-chars-forward "^{}[]\\\\" limit)
(when (and (< (point) limit) (not (TeX-in-comment)))
(setq char (char-after))
(forward-char)
(cond ((eq char ?\{)
(setq count (+ count TeX-brace-indent-level)))
((eq char ?\})
(setq count (- count TeX-brace-indent-level)))
((eq char ?\[)
(setq count (+ count TeX-brace-indent-level)))
((eq char ?\])
(setq count (- count TeX-brace-indent-level)))
((eq char ?\\)
(when (< (point) limit)
(forward-char) t))))))
count)))
(advice-add 'TeX-brace-count-line :override #'my-TeX-brace-count-line))
#+end_src
** Org-mode
*** [[info:org#Activation][Activation (info)]]
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
;; Inspect:
;; function with "C-h f"
;; symbols with "C-h o"
;; variables with "C-h v"
(global-set-key (kbd "C-c a") #'org-agenda)
(global-set-key (kbd "C-c c") #'org-capture)
(global-set-key (kbd "C-c l") #'org-store-link)
(global-set-key (kbd "C-c C-l") #'org-insert-link-global)
#+end_src
*** Customization
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(custom-set-variables
'(org-babel-python-command "python -E")
'(org-babel-load-languages '((C . t)
(calc . t)
(dot . t)
(emacs-lisp . t)
(eshell . t)
(fortran . t)
(gnuplot . t)
(latex . t)
(lisp . t)
(maxima . t)
(org . t)
(perl . t)
(python . t)
(scheme . t)
(shell . t)))
'(org-cite-export-processors '((latex biblatex)
(t csl)))
'(org-cite-global-bibliography '("~/VCS/research/refs.bib"))
'(org-file-apps '((auto-mode . emacs)
(directory . emacs)
("\\.mm\\'" . default)
("\\.x?html?\\'" . default)
("\\.pdf\\'" . emacs)))
'(org-confirm-babel-evaluate nil)
'(org-latex-compiler "lualatex")
'(org-latex-hyperref-template nil)
'(org-latex-listings 'minted)
'(org-latex-logfiles-extensions '("blg" "lof" "log" "lot" "out" "toc"))
'(org-latex-prefer-user-labels t)
'(org-modules '(ol-bibtex
ol-doi
ol-eww
ol-info
org-id
org-protocol
org-tempo))
'(org-src-fontify-natively t)
'(org-src-window-setup 'current-window)
'(org-structure-template-alist
'(("a" . "export ascii")
("c" . "center")
("C" . "comment")
("e" . "example")
("E" . "export")
("h" . "export html")
("l" . "export latex")
("q" . "quote")
("s" . "src")
("p" . "src python :session :async")
("v" . "verse"))))
#+end_src
*** [[info:org#Citation export processors][Citation export processors (info)]]
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(with-eval-after-load 'oc
(require 'oc-biblatex)
(require 'oc-csl))
#+end_src
*** [[https://tecosaur.github.io/emacs-config/#translate-capital-keywords][Translate capital keywords (old) to lower case (new)]]
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(with-eval-after-load 'org
(defun org-syntax-convert-keyword-case-to-lower ()
"Convert all #+KEYWORDS to #+keywords."
(interactive)
(save-excursion
(goto-char (point-min))
(let ((count 0)
(case-fold-search nil))
(while (re-search-forward "^[ \t]*#\\+[A-Z_]+" nil t)
(unless (s-matches-p "RESULTS" (match-string 0))
(replace-match (downcase (match-string 0)) t)
(setq count (1+ count))))
(message "Replaced %d keywords" count)))))
#+end_src
*** [[info:org#Advanced Export Configuration][Advanced Export Configuration (info)]]
Stolen from [[https://git.sr.ht/~bzg/org-contrib/tree/master/item/lisp/ox-extra.el][ox-extra.el]]
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(with-eval-after-load 'ox
(defun my-org-latex-header-blocks-filter (backend)
(when (org-export-derived-backend-p backend 'latex)
(let ((blocks
(org-element-map
(org-element-parse-buffer 'greater-element nil) 'export-block
(lambda (block)
(let ((type (org-element-property :type block))
(header (org-export-read-attribute :header block :header)))
(when (and (string= type "LATEX") (string= header "yes"))
block))))))
;; Set point to where to insert LaTeX header lines after
;; deleting the block.
(mapc (lambda (block)
(goto-char (org-element-property :post-affiliated block))
(let ((lines
(split-string (org-element-property :value block) "\n")))
(delete-region (org-element-property :begin block)
(org-element-property :end block))
(dolist (line lines)
(insert (concat "#+latex_header: "
(replace-regexp-in-string "\\` *" "" line)
"\n")))))
;; Reverse to go upwards to avoid wrecking the numeric
;; positions earlier in the file.
(reverse blocks)))))
(add-hook 'org-latex-header-blocks-filter #'my-org-latex-header-blocks-filter))
#+end_src
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(with-eval-after-load 'ox-latex
(mapc (lambda (item)
(add-to-list 'org-latex-classes item))
'(;; The postfixes +1, +2, +3, -1, -2, and -3 denote:
;; +1 => [DEFAULT-PACKAGES]
;; +2 => [PACKAGES]
;; +3 => [EXTRA]
;; -1 => [NO-DEFAULT-PACKAGES]
;; -2 => [NO-PACKAGES]
;; -3 => [NO-EXTRA]
("elsarticle-1+2+3" ; Elsevier journals
"\\documentclass{elsarticle}
[NO-DEFAULT-PACKAGES]
[PACKAGES]
[EXTRA]"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
("article-1+2+3"
"\\documentclass{article}
[NO-DEFAULT-PACKAGES]
[PACKAGES]
[EXTRA]"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
("report-1+2+3"
"\\documentclass[11pt]{report}
[NO-DEFAULT-PACKAGES]
[PACKAGES]
[EXTRA]"
("\\part{%s}" . "\\part*{%s}")
("\\chapter{%s}" . "\\chapter*{%s}")
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
("book-1+2+3"
"\\documentclass[11pt]{book}
[NO-DEFAULT-PACKAGES]
[PACKAGES]
[EXTRA]"
("\\part{%s}" . "\\part*{%s}")
("\\chapter{%s}" . "\\chapter*{%s}")
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")))))
#+end_src
*** Evaluate source blocks on loading
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(defun my-org-eval-blocks-named (name)
"Evaluate all source blocks named NAME."
(when (eq major-mode 'org-mode)
(let ((blocks
(org-element-map
(org-element-parse-buffer 'greater-element nil) 'src-block
(lambda (block)
(when (string= name (org-element-property :name block))
block)))))
(dolist (block blocks)
(goto-char (org-element-property :begin block))
(org-babel-execute-src-block)))))
;; Emacs looks for "Local variables:" after the last "C-q C-j C-q C-l".
(add-to-list 'safe-local-eval-forms
'(apply 'my-org-eval-blocks-named '("elisp-setup")))
(add-to-list 'safe-local-eval-forms
'(apply 'my-org-eval-blocks-named '("python-setup")))
#+end_src
** Citing bibliography
:PROPERTIES:
:CUSTOM_ID: sec:citing-bibliography
:END:
[[https://github.com/bdarcus/citar][Citar]] provides a completing-read front-end to browse and act on BibTeX,
BibLaTeX, and CSL JSON bibliographic data, and LaTeX, markdown, and org-cite
editing support.
[[https://github.com/bdarcus/citar][Citar]] -- in combination with vertico, embark, and marginalia -- provides quick
filtering and selecting of bibliographic entries from the minibuffer, and the
option to run different commands on those selections.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(when (cl-every #'fboundp '(citar-insert-citation
citar-insert-preset
citar-org-activate
citar-org-follow
citar-org-insert))
(custom-set-variables
'(org-cite-activate-processor 'citar)
'(org-cite-follow-processor 'citar)
'(org-cite-insert-processor 'citar))
(global-set-key (kbd "C-c b") #'citar-insert-citation)
(define-key minibuffer-local-map (kbd "M-b") #'citar-insert-preset))
#+end_src
* Editing
** Synchronal multiple-region editing
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(require 'iedit nil 'noerror))
#+end_src
** Extraneous whitespace trimming
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(when (require 'ws-butler nil 'noerror)
(custom-set-variables
'(ws-butler-keep-whitespace-before-point nil))
(add-hook 'prog-mode-hook #'ws-butler-mode)
(add-hook 'text-mode-hook #'ws-butler-mode)))
#+end_src
** Smart character-pair handling
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
(when (require 'smartparens-config nil 'noerror)
;; Requiring smartparens-config disables pairing of the quote
;; character for lisp modes, contrary to requiring smartparens.
(custom-set-variables
'(sp-base-key-bindings 'sp)
'(sp-override-key-bindings '(("C-(" . sp-backward-barf-sexp)
("C-)" . sp-forward-slurp-sexp))))
(add-hook 'LaTeX-mode-hook #'turn-off-smartparens-mode)
(add-hook 'prog-mode-hook #'turn-on-smartparens-mode)
(add-hook 'text-mode-hook #'turn-on-smartparens-mode)
(add-hook 'emacs-lisp-mode-hook #'turn-on-smartparens-strict-mode)
(add-hook 'ielm-mode-hook #'turn-on-smartparens-strict-mode)
(add-hook 'python-mode-hook #'turn-on-smartparens-strict-mode)
;; https://xenodium.com/emacs-smartparens-auto-indent/index.html
(defun indent-between-pair (&rest _ignored)
(newline)
(indent-according-to-mode)
(forward-line -1)
(indent-according-to-mode))
(sp-local-pair 'prog-mode "(" nil :post-handlers '((indent-between-pair "RET")))
(sp-local-pair 'prog-mode "[" nil :post-handlers '((indent-between-pair "RET")))
(sp-local-pair 'prog-mode "{" nil :post-handlers '((indent-between-pair "RET")))
(show-smartparens-global-mode +1)))
#+end_src
** Operators
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(when (fboundp 'electric-operator-mode)
(add-hook 'c-mode-common #'electric-operator-mode)
(add-hook 'python-mode-hook #'electric-operator-mode))
#+end_src
** Smart snippets
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(when (require 'yasnippet nil 'noerror)
(custom-set-variables
'(yas-alias-to-yas/prefix-p nil))
(yas-global-mode +1))
#+end_src
* Coding
#+begin_src emacs-lisp
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs '(python-mode "pylsp")))
#+end_src
** Python coding
Here, the focus is on three ways to extend Emacs's built-in ~python-mode~ out of
the options listed on the [[https://www.emacswiki.org/emacs/PythonProgrammingInEmacs][Python Programming in Emacs]] wiki page:
1. [[https://elpy.readthedocs.io/en/latest/][Elpy]] is an opinionated Python integrated development environment with its own
Python server and its own server protocol. Its main disadvantages are:
1. It requires a high number of Elisp and Python packages.
2. It imposes its way making it hard to do otherwise for beginners.
3. It requires a new maintainer, because the current maintainer has no time
anymore for adding new features or squashing hard bugs.
Its main advantages are:
1. Its usability out of the box.
2. Its documentation quality.
3. Its compatibility with the Org source block editing mode.
2. [[https://emacs-lsp.github.io/lsp-mode/][LSP Mode - Language Server Protocol support for Emacs]] with its [[https://emacs-lsp.github.io/lsp-ui/][LSP-UI]] user
interface extensions. According to [[https://github.com/emacs-lsp/lsp-mode/blob/master/docs/manual-language-docs/lsp-org.md][Literate programming using LSP and
org-mode(alpha)]], this package tries to be compatible with the Org source
block editing mode, but it is not ready for daily usage.
3. [[https://github.com/joaotavora/eglot][Eglot - Emacs polyGLOT: an Emacs LSP client that stays out of your way]]. The
maintainer also contributes to Emacs itself and has a deep understanding of
[[https://sheer.tj/the_way_of_emacs.html][the Way of Emacs]]. He refuses to add new features without seeing how they fit
into [[https://sheer.tj/the_way_of_emacs.html][the Way of Emacs]] as this discussion at [[https://github.com/joaotavora/eglot/issues/523][Eglot github issue: org-mode
source code blocks]] shows.
[[https://emacs.stackexchange.com/questions/45164/does-org-have-any-inverse-tangle-operations-e-g-for-collaborating-with-non-or][Does org have any "inverse-tangle" operations e.g. for collaborating with non-org users?]]
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src python :tangle example.py :comments link
import numpy
import astropy.units as apu
a = numpy.linspace(0, 10, num=11)
q = apu.Quantity(a, apu.meter)
print(q)
#+end_src
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(custom-set-variables
'(python-shell-interpreter-args "-i -E"))
(when (and (executable-find "pyenv")
(require 'pyenv-mode nil 'noerror))
(pyenv-mode +1)
(pyenv-mode-set "3.9.8/envs/python-3.9.8"))
#+end_src
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(with-eval-after-load 'info
(add-to-list 'Info-directory-list
(expand-file-name "~/.local/share/info")))
#+end_src
Look into:
1. [[https://github.com/douglasdavis/numpydoc.el/blob/main/numpydoc.el][Emacs extension to insert numpy style docstrings in function definitions]]
* Appearance
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
;; Set face attributes.
(cond
((eq system-type 'darwin)
(set-face-attribute 'default nil :family "Hack" :height 120)
(set-face-attribute 'fixed-pitch nil :family "Hack")
(set-face-attribute 'variable-pitch nil :family "FiraGo"))
((eq system-type 'gnu/linux)
(set-face-attribute 'default nil :family "Hack" :height 110)
(set-face-attribute 'fixed-pitch nil :family "Hack")
(set-face-attribute 'variable-pitch nil :family "FiraGo"))
(t
(set-face-attribute 'default nil :family "Hack" :height 110)
(set-face-attribute 'fixed-pitch nil :family "Hack")
(set-face-attribute 'variable-pitch nil :family "DejaVu Sans"))))
#+end_src
This setup prefers the ~leuven~ and ~leuven-dark~ themes from [[https://melpa.org/#/][MELPA]], because the
very popular ~modus-operandi~ and ~modus-vivendi~ themes feel quirky: for
instance those themes fail to display ~hl-line-mode~ with Emacs-27.2 on Darwin.
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
;; Try to detect `leuven-theme` from MELPA.
(when (fboundp 'leuven-scale-font)
(custom-set-variables
'(leuven-scale-org-agenda-structure nil)
'(leuven-scale-org-document-title nil)
'(leuven-scale-outline-headlines nil)
'(leuven-scale-volatile-highlight nil)))
(load-theme 'leuven 'no-confirm nil))
#+end_src
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp :tangle no
(unless noninteractive
(custom-set-variables
'(modus-themes-hl-line 'underline)
'(modus-themes-intense-markup 't))
(when (and (version< emacs-version "28.0")
(require 'modus-themes nil 'noerror))
(modus-themes-load-themes)))
#+end_src
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(unless noninteractive
;; https://karthinks.com/software/batteries-included-with-emacs/
;; https://www.reddit.com/r/emacs/comments/jwhr6g/batteries_included_with_emacs/
(defun my-pulse-one-line (&rest _)
"Pulse the current line."
(let ((pulse-iterations 16)
(pulse-delay 0.1))
(pulse-momentary-highlight-one-line (point))))
(dolist (command '(scroll-up-command
scroll-down-command
recenter-top-bottom
other-window))
(advice-add command :after #'my-pulse-one-line)))
#+end_src
* Applications
** Feed reader
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(autoload 'elfeed "elfeed" nil t)
(global-set-key (kbd "C-x w") #'elfeed)
(with-eval-after-load 'elfeed
(custom-set-variables
'(elfeed-feeds
'(("http://www.howardism.org/index.xml" h-abrams)
("https://ambrevar.xyz/atom.xml" p-neirhardt)
("https://emacshorrors.com/feed.atom" v-schneidermann)
("https://emacsninja.com/emacs.atom" v-schneidermann)
("https://feeds.feedburner.com/InterceptedWithJeremyScahill" j-scahill)
("https://nullprogram.com/feed/" c-wellons)
("https://oremacs.com/atom.xml" o-krehel)
("https://planet.emacslife.com/atom.xml" planet-emacs)
("https://protesilaos.com/codelog.xml" p-stavrou)
("https://sachachua.com/blog/category/emacs/feed" s-chua)
("https://sciencescitoyennes.org/feed/" sciences)
("https://updates.orgmode.org/feed/updates" org-updates)
("https://www.aclu.org/taxonomy/feed-term/2152/feed" aclu)
("https://www.bof.nl/rss/" bof)
("https://www.democracynow.org/podcast-video.xml" dn)
("https://www.laquadrature.net/fr/rss.xml" lqdn)
("https://www.lemonde.fr/blog/huet/feed/" sciences)))))
#+end_src
** Multi-media system
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(custom-set-variables
'(emms-mode-line-format "")
'(emms-player-list '(emms-player-mpd emms-player-mpv))
`(emms-player-mpd-music-directory ,(expand-file-name "~/Music"))
'(emms-player-mpd-server-name "localhost")
'(emms-player-mpd-server-port "6600")
'(emms-player-mpd-verbose t)
'(emms-playing-time-display-format " %s ")
'(emms-playlist-mode-center-when-go t))
(defun my-emms-print-metadata-find ()
(require 'find-func)
(locate-file
"emms-print-metadata"
(expand-file-name
"src"
(file-name-directory (find-library-name "emms")))
exec-suffixes #'file-executable-p))
(with-eval-after-load 'emms
(require 'emms-info-libtag)
(let ((emms-print-metadata (my-emms-print-metadata-find)))
(when emms-print-metadata
(custom-set-variables
'(emms-info-functions nil)
`(emms-info-libtag-program-name ,emms-print-metadata))
(add-hook 'emms-info-functions #'emms-info-libtag))))
(with-eval-after-load 'elfeed-show
(when (require 'emms-setup nil 'noerror)
(emms-all)))
(autoload 'emms-streams "emms-streams" nil 'interactive)
(with-eval-after-load 'emms-streams (emms-all))
#+end_src
* [[info:emacs#Init File][Init File (info)]] footer
:PROPERTIES:
:CUSTOM_ID: sec:init-file-footer
:END:
#+attr_latex: :options bgcolor=LightGoldenrodYellow
#+begin_src emacs-lisp
(provide 'init)
;; Emacs looks for "Local variables:" after the last "C-q C-j C-q C-l".
;; Local Variables:
;; indent-tabs-mode: nil
;; End:
;;; init.el ends here
#+end_src
* Compile with [[https://www.personal.psu.edu/jcc8/latexmk/][latexmk]]
:PROPERTIES:
:CUSTOM_ID: sec:compile-with-latexmk
:END:
The local variable ~compile-command~ (only visible in =org= files, but not in
=html= and =pdf= files) below shows how to use the =latexmkrc= file in section
[[#sec:latexmk-compilation-loop][usage: latexmk compilation loop]].
# Emacs looks for "Local variables:" after the last "C-q C-j C-q C-l".
# Local Variables:
# compile-command: "latexmk -interaction=nonstopmode -lualatex -pvc -shell-escape README.tex"
# fill-column: 80
# End: