commit 5afb84a62fe41fd1d7a1b5bb24299f89c3846631 Author: Gerard Vermeulen Date: Mon Nov 29 13:14:56 2021 +0100 Reset my Emacs configuration. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a53de0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.el +*.py +latexmkrc +org-store-link +/eln-cache +/elpa-* +/quelpa +/var diff --git a/1ST-ME b/1ST-ME new file mode 100755 index 0000000..47b4949 --- /dev/null +++ b/1ST-ME @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +emacs --batch --eval "(require 'org)" --eval '(org-babel-tangle-file "README.org")' + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# sh-basic-offset: 2 +# End: diff --git a/README.org b/README.org new file mode 100644 index 0000000..c4839e3 --- /dev/null +++ b/README.org @@ -0,0 +1,1148 @@ +#+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 - Milkypostman’s 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 " 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: