#+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} * Quick start :PROPERTIES: :CUSTOM_ID: sec:quick-start :END: Backup your =~/.emacs.d= directory to execute the following commands: #+begin_src shell :noeval :tangle no cd ~ git clone ccdr@mercury.grenoble.cnrs.fr:SERVER/.emacs.d make --directory=.emacs.d init #+end_src After its invokation, Emacs will install a minimal set of packages. Now, you have the option to install all optional packages using the command =my-install-optional-packages=, but you can do this any time, or you can install any package using the command =package-install= whenever you like. Quit Emacs and invoke Emacs again. * 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 to tangle (or export) source blocks from [[info:org#Top][org]] files. This file contains source blocks to produce the files =early-init.el=, =init.el=, =latexmkrc=, =org-store-link=, and =example.py= by 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 [[https://en.wikipedia.org/wiki/PDF][PDF]] file that link back to the original [[https://www.latex-project.org/][LaTeX]] file of the document. An example of my work-flow are the steps 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. Here follows a list of interesting Emacs configurations: 1. [[https://github.com/alhassy/emacs.d][Musa Al-hassy's configuration]] is an impressive example of producing the Emacs initialization files and other files by tangling an [[info:org#Top][org]] file. His methodology is impressive, as his [[https://alhassy.github.io/ElispCheatSheet/][Elisp Cheat Sheet]] and [[https://alhassy.github.io/org-special-block-extras/][org-special-block-extra package]] show. To me, this is a configuration to admire, but his methodology is way over my head. 2. [[https://github.com/oantolin/emacs-config][Omar Antolín Camarena's configuration]] exploits built-in packages, Omar's own small packages, and large external packages. Omar is the author of [[https://github.com/oantolin/orderless][orderless]] and [[https://github.com/oantolin/embark][embark]]. I have stolen his idea of using ~custom-set-variables~. 3. [[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. 4. [[https://sachachua.com/dotemacs/][Sacha Chua's configuration]] is a practical 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. 5. [[https://github.com/purcell/emacs.d][Steve Purcell's configuration]] is well organized, a showcase of readable code, as well helpful commit and issue histories. See for instance the discussion on [[https://github.com/purcell/emacs.d/issues/778][the correctness of order of company candidates in Emacs lisp mode]]. 6. [[https://github.com/tecosaur/emacs-config][Timothy E. Chapman's configuration]] * [[info:emacs#Early Init File][Early Init File (info)]] :PROPERTIES: :CUSTOM_ID: sec:early-init-file :END: #+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 load-prefer-newer t) (require 'no-littering nil 'noerror) (provide 'early-init) ;; Emacs looks for "Local variables:" after the last "?\n?\f". ;; Local Variables: ;; indent-tabs-mode: nil ;; End: ;;; earl-init.el ends here #+end_src In order to get help in understanding the code block above in a buffer showing the original [[info:org#Top][Org]] source file, move point (or cursor) to one of the items of the list the and type =C-c C-c=: 1. src_emacs-lisp[:exports code]{(describe-variable #'load-prefer-newer t)} 2. src_emacs-lisp[:exports code]{(apropos-library "no-littering")} 3. src_emacs-lisp[:exports code]{(find-function #'hack-local-variables)} to execute the code between the curly braces for access to help. This shows why *Emacs is a self-documenting editor.* * [[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. A tutorial of how to use those reader macros is the [[https://mullikine.github.io/posts/macro-tutorial/][didactic emacs-lisp macro example]]. Because of the ~custom-set-variable~ function call, the [[info:emacs#Init File][init file (info)]] does not load the ~custom-file~ as [[info:emacs#Saving Customizations][saving customizations (info)]] recommends. #+attr_latex: :options bgcolor=LightGoldenrodYellow #+begin_src emacs-lisp ;;; init.el --- user init file -*- lexical-binding: t -*- ;;; Commentary: ;;; Code: (require 'cl-lib) (custom-set-variables '(after-save-hook #'executable-make-buffer-file-executable-if-script-p) '(column-number-mode t) '(cursor-type 'box) `(custom-file ,(make-temp-file "emacs-custom-")) '(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-archives '(("gnu" . "https://elpa.gnu.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/") ("melpa" . "https://melpa.org/packages/"))) `(package-selected-packages `( ,@(when (version< emacs-version "28.0") '(org)) ; plain text thought organizer anaconda-mode ; strangles python-mode auctex ; Aalborg University Center TeX blacken ; Black Python-code formatter client citar ; bibliography handling citeproc ; bibliography handling company-anaconda ; complete anything in anaconda-mode consult ; consult completing-read eglot ; Emacs polyGLOT LSP client embark ; act on any buffer selection htmlize ; convert buffer contents to HTML leuven-theme ; beautiful color theme marginalia ; minibuffer margin notes orderless ; Emacs completion style pdf-tools ; interactive docview replacement pdf-view-restore ; add view history to pdf-tools pyenv-mode ; Python environment selector quelpa ; install Emacs packages from source vertico)) ; VERTical Interactive Completion '(python-indent-guess-indent-offset nil) '(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: 1. The [[https://elpa.gnu.org/][GNU Emacs Lisp Package Archive]] 2. 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. The order of the next two package-bootstrapping blocks matters because first block prepares Emacs for the second 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. The call src_emacs-lisp[:exports_code]{(package-install-selected-packages)} checks the installation status of all packages in src_emacs-lisp[:exports_code]{package-selected-packages} and installs the missing packages after the user has agreed to its prompt. Finally, ~my-install-packages~ facilitates 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. (unless (require 'no-littering nil 'noerror) (package-refresh-contents) (package-install 'no-littering) (require 'no-littering)) (unless noninteractive (package-install-selected-packages)) (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 The next source code block facilitates installlation of the optional package, The user can run src_emacs-lisp[:exports_code]{(my-install-optional-packages)} to install all optional packages, or he can install each package one by one. #+attr_latex: :options bgcolor=LightGoldenrodYellow #+begin_src emacs-lisp ;; The is the 2nd 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 markdown-mode ; markdown text mode nov ; EPUB reader rainbow-mode ; set background color to color string smartparens ; smart editing of character pairs toml-mode ; Tom's Obvious Minimal Language mode 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][Using Emacs as a server (info)]] :PROPERTIES: :CUSTOM_ID: sec:using-emacs-server :END: 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 The next two configuration blocks show how to use ~emacsclient~ to: 1. Install an asynchronous (or background) loop of saving a LaTeX file, compiling it, and redisplaying the output in Emacs. 2. Make [[https://qutebrowser.org][qutebrowser]] send html links with document titles to Emacs. ** LaTeX save-compile-display loop :PROPERTIES: :CUSTOM_ID: sec:latex-save-compile-display-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 are compatible. The local variable ~compile-command~ in the [[#sec:local-variables][local variables]] section (only visible in =org= files, but not in =html= and =pdf= files) shows how to use the =latexmkrc= file.. #+attr_latex: :options bgcolor=LightGoldenrodYellow #+begin_src perl :tangle latexmkrc :comments none # 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 ist run.xml"; sub makeglossaries { my ($name, $path) = fileparse( $$Psource ); return system "makeglossaries -d '$path' '$name'"; } # Emacs looks for "Local variables:" after the last "?\n?\f". # Local Variables: # mode: perl # End: #+end_src ** [[https://qutebrowser.org/doc/userscripts.html][Qutebrowser userscript]] :PROPERTIES: :CUSTOM_ID: sec:qutebrowser-userscript :END: 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. [[https://cestlaz.github.io/post/using-emacs-80-vertico/][Using Vertico, Marginalia, Consult, and Embark]] links to a video demonstration. Vertico integrates well with fully supported complementary packages to enrich the completion UI: 1. [[info:orderless#Top][Orderless (info)]] for an advanced completion style, 2. [[info:embark#Top][Embark (info)]] for minibuffer actions with context menus, 3. [[info:marginalia#Top][Marginalia (info)]] for rich annotations in the minibuffer, and 4. [[info:consult#Top][Consult (info)]] for useful search and navigation commands, where the order is that of [[https://github.com/bdarcus/citar#installation][enhancing citar's experience]] and the configuration steps below. Finally, [[https://company-mode.github.io/][company: a modular complete-anything framework for Emacs]] fills another niche than the five packages above. ** [[info:vertico#Top][Vertico (info)]] :PROPERTIES: :CUSTOM_ID: sec:vertico-configuration :END: #+attr_latex: :options bgcolor=LightGoldenrodYellow #+begin_src emacs-lisp (unless noninteractive (when (fboundp 'vertico-mode) (vertico-mode +1))) #+end_src ** [[info:orderless#Top][Orderless (info)]] :PROPERTIES: :CUSTOM_ID: sec:orderless-configuration :END: #+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 ** [[info:embark#Top][Embark (info)]] :PROPERTIES: :CUSTOM_ID: sec:embark-configuration :END: #+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:marginalia#Top][Marginalia (info)]] :PROPERTIES: :CUSTOM_ID: sec:marginalia-configuration :END: #+attr_latex: :options bgcolor=LightGoldenrodYellow #+begin_src emacs-lisp (unless noninteractive (when (fboundp 'marginalia-mode) (marginalia-mode +1))) #+end_src ** [[info:consult#Top][Consult (info)]] :PROPERTIES: :CUSTOM_ID: sec:consult-configuration :END: #+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 ** [[https://company-mode.github.io/][Company: a modular complete anything framework for Emacs]] :PROPERTIES: :CUSTOM_ID: sec:company-configuration :END: #+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))) (with-eval-after-load 'pdf-tools (when (fboundp 'pdf-view-restore-mode) (add-hook 'pdf-view-mode-hook #'pdf-view-restore-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-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)]] :PROPERTIES: :CUSTOM_ID: sec:citation-export-processors :END: #+attr_latex: :options bgcolor=LightGoldenrodYellow #+begin_src emacs-lisp (with-eval-after-load 'oc (require 'oc-biblatex) (require 'oc-csl)) #+end_src *** [[info:org#External Links][Export external links (info)]] :PROPERTIES: :CUSTOM_ID: sec:export-external-links :END: #+begin_src emacs-lisp (with-eval-after-load 'ol (org-link-set-parameters "ac*" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\gls*{%s}" path)) (_ path)))) (org-link-set-parameters "cite" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\cite{%s}" path)) (_ path)))) (org-link-set-parameters "eqref" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\eqref{%s}" path)) (_ path)))) (org-link-set-parameters "label" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\label{%s}" path)) (_ path)))) (org-link-set-parameters "ref" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\ref{%s}" path)) (_ path))))) #+end_src In order to export the [[info:org#External Links][info links (info)]] in this document to =html= correctly, modify the constant ~org-info-other-documents~ defined in =ol-info.el=. Note: how to make it work without tangling? #+begin_src emacs-lisp (add-hook 'org-mode-hook (lambda () (when (boundp 'org-info-other-documents) (dolist (recipe '(("consult" . "https://github.com/minad/consult") ("embark" . "https://github.com/oantolin/embark") ("marginalia" . "https://github.com/minad/marginalia") ("orderless" . "https://github.com/oantolin/orderless") ("vertico" . "https://github.com/minad/vertico"))) (cl-pushnew recipe org-info-other-documents :test #'equal))))) #+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-export-ignore-headline-filter (_) "Ignore all headlines with the \":ignore:\" tag." (org-map-entries (lambda () (delete-region (point) (line-beginning-position 2))) ":ignore:")) (defun my-org-latex-header-blocks-filter (backend) "Convert marked LaTeX export blocks to \"#+latex_header: \" lines. The marker is a line \"#+header: :header yes\" preceding the block. For instance, the LaTeX export block ,#+header: :header yes ,#+begin_export latex % This line converts to a LaTeX header line. ,#+end_export converts to \"#+latex_header: % This line converts to a LaTeX header line.\"." (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)))))) (mapc (lambda (block) ;; Set point to where to insert LaTeX header lines ;; after deleting the 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 list of ;; block positions in the file that would occur in case ;; of going downwards. (reverse blocks))))) (defun my-activate-buffer-local-org-export-filters () "Activate my export filters locally in the current buffer." (interactive) (add-hook 'org-export-before-parsing-hook #'my-org-export-ignore-headline-filter nil 'local) (add-hook 'org-export-before-parsing-hook #'my-org-latex-header-blocks-filter nil 'local))) #+end_src #+name: emacs-lisp-setup #+begin_src emacs-lisp :tangle no (with-eval-after-load 'ox (my-activate-buffer-local-org-export-filters)) #+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 "?\n?\f". (add-to-list 'safe-local-eval-forms '(apply 'my-org-eval-blocks-named '("emacs-lisp-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 ** [[https://www.emacswiki.org/emacs/DisabledCommands][Enable disabled commands and inform]] :PROPERTIES: :CUSTOM_ID: sec:enable-disabled-commands :END: Execute src_emacs-lisp[:exports code]{(find-library "novice")} to see how Emacs prevents new users from shooting themselves in the feet. #+begin_src emacs-lisp (setq disabled-command-function (defun my-enable-this-command (&rest _args) "Called when a disabled command is executed. Enable it and re-execute it." (put this-command 'disabled nil) (message "You typed %s. %s was disabled until now." (key-description (this-command-keys)) this-command) (sit-for 0) (call-interactively this-command))) #+end_src ** [[info:emacs#Narrowing][Narrowing]] :PROPERTIES: :CUSTOM_ID: sec:narrowing :END: [[https://endlessparentheses.com/emacs-narrow-or-widen-dwim.html][Emacs narrow-or-widen-dwim]] #+begin_src emacs-lisp (defun narrow-or-widen-dwim (p) "Widen if buffer is narrowed, narrow-dwim otherwise. Dwim means: region, org-src-block, org-subtree, or defun, whichever applies first. Narrowing to org-src-block actually calls `org-edit-src-code'. With prefix P, don't widen, just narrow even if buffer is already narrowed." (interactive "P") (declare (interactive-only)) (cond ((and (buffer-narrowed-p) (not p)) (widen)) ((and (bound-and-true-p org-src-mode) (not p)) (org-edit-src-exit)) ((region-active-p) (narrow-to-region (region-beginning) (region-end))) ((derived-mode-p 'org-mode) (or (ignore-errors (org-edit-src-code)) (ignore-errors (org-narrow-to-block)) (org-narrow-to-subtree))) ((derived-mode-p 'latex-mode) (LaTeX-narrow-to-environment)) ((derived-mode-p 'tex-mode) (TeX-narrow-to-group)) (t (narrow-to-defun)))) (define-key ctl-x-map (kbd "C-n") #'narrow-or-widen-dwim) #+end_src ** 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 ** Electric operators :PROPERTIES: :CUSTOM_ID: sec:electric-operators :END: #+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")) (setq-default eglot-workspace-configuration `((:pylsp . (:plugins (:jedi_completion (:eager nil)))) (:pylsp . (:plugins (:jedi_completion (:cache_for ,(vconcat '("astropy" "numpy" "scipy"))))))))) #+end_src ** Python coding The [[https://www.emacswiki.org/emacs/PythonProgrammingInEmacs][Python Programming in Emacs]] wiki page lists options to enhance Emacs's built-in ~python-mode~. Here, the focus is on two packages: 1. [[https://github.com/pythonic-emacs/anaconda-mode][Anaconda - code navigation, documentation lookup, and completion for Python]]. 2. [[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 on [[https://github.com/joaotavora/eglot/issues/523][org-mode source code blocks]] shows. The snippet below initializes [[https://github.com/pythonic-emacs/anaconda-mode][anaconda]]. See [[https://github.com/jorgenschaefer/elpy/blob/8d0de310d41ebf06b22321a8534546447456870c/elpy.el#L2775][elpy-module-company]] for how to handle ~company-backends~ as a local variable and the call to ~advice~ opens Python org-mode edit-buffers in ~anaconda-mode~. #+begin_src emacs-lisp (with-eval-after-load 'python (with-eval-after-load 'company (when (and (fboundp 'anaconda-mode) (fboundp 'company-anaconda)) (defun my-disable-anaconda-mode () (when (derived-mode-p 'python-mode) (anaconda-mode -1) (set (make-local-variable 'company-backends) (delq 'company-anaconda (mapcar #'identity company-backends))) (anaconda-eldoc-mode -1))) (defun my-enable-anaconda-mode () (when (derived-mode-p 'python-mode) (anaconda-mode +1) (set (make-local-variable 'company-backends) (cons 'company-anaconda (delq 'company-semantic (delq 'company-capf (mapcar #'identity company-backends))))) (anaconda-eldoc-mode (if (file-remote-p default-directory) -1 1)))))) (unless (and (fboundp 'my-disable-anaconda-mode) (fboundp 'my-enable-anaconda-mode)) (when (fboundp 'anaconda-mode) (defun my-disable-anaconda-mode () (when (derived-mode-p 'python-mode) (anaconda-mode -1) (anaconda-eldoc-mode -1))) (defun my-enable-anaconda-mode () (when (derived-mode-p 'python-mode) (anaconda-mode +1) (anaconda-eldoc-mode (if (file-remote-p default-directory) -1 1)))))) (when (fboundp 'my-enable-anaconda-mode) (advice-add 'org-edit-src-code :after #'my-enable-anaconda-mode)) (when (and (fboundp 'my-disable-anaconda-mode) (fboundp 'my-enable-anaconda-mode)) (defun my-toggle-anaconda-mode () (interactive) (if (bound-and-true-p anaconda-mode) (my-disable-anaconda-mode) (my-enable-anaconda-mode))))) #+end_src #+attr_latex: :options bgcolor=LightGoldenrodYellow #+begin_src python :tangle example.py :comments link import numpy import astropy.units as apu a = numpy.arange(0, 11) 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.9/envs/python-3.9.9")) #+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 See the [[https://protesilaos.com/codelog/2020-09-05-emacs-note-mixed-font-heights/][note on mixed font heights in Emacs]] for how to setup fonts properly. It boils down to two rules: 1. The height of the default face must be an integer number to make the height a physical quantity. 2. The heights of all other faces must be real numbers to scale those heights with respect to the height of the face (those heights default to 1.0 for no scaling). The next source code blocks implement those rules. #+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 In case of proper initialization of all face heigths, font scaling is easy as the next source code block shows. #+begin_src emacs-lisp (defun my-set-default-face-height () "Set the default face height in all current and future frames. Scale all other faces with a height that is a real number." (interactive) (let* ((prompt (format "face heigth (%s): " (face-attribute 'default :height))) (height (string-to-number (completing-read prompt (mapcar #'number-to-string (number-sequence 50 200 10)) nil 'require-match)))) (message "Setting the height of the default face to %s" new) (set-face-attribute 'default nil :height new))) #+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~ properly with Emacs-27.2 on Darwin. [[https://emacs.stackexchange.com/questions/17431/how-do-i-change-portions-of-a-custom-theme][How to change custom theme faces]] #+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 1.0) '(leuven-scale-org-document-title 1.0) '(leuven-scale-outline-headlines 1.0) '(leuven-scale-volatile-highlight 1.0))) (defun my-leuven-hook-function () (when (member 'leuven custom-enabled-themes) (let ((custom-inhibit--theme-enable nil) (ol1 (list :height 1.0 :weight 'bold :foreground "#3C3C3C" :background "#F0F0F0")) (ol2 (list :height 1.0 :weight 'bold :foreground "#123555" :background "#E5F4FB"))) (custom-theme-set-faces 'leuven `(font-latex-sectioning-2-face ((t ,ol1))) `(font-latex-sectioning-3-face ((t ,ol2))) `(info-title-1 ((t ,ol1))) `(markdown-header-face-1 ((t ,ol1))) `(markdown-header-face-2 ((t ,ol2))) `(org-level-1 ((t ,ol1))) `(org-level-2 ((t ,ol2))) `(outline-1 ((t ,ol1))) `(outline-2 ((t ,ol1))))) (enable-theme 'leuven))) (load-theme 'leuven 'no-confirm nil) (dolist (hook '(Info-mode-hook latex-mode-hook markdown-mode-hook org-mode-hook outline-mode-hook)) (add-hook hook #'my-leuven-hook-function))) #+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 "?\n?\f". ;; Local Variables: ;; indent-tabs-mode: nil ;; End: ;;; init.el ends here #+end_src * Local variables linking back to [[#sec:latex-save-compile-display-loop][LaTeX save-compile-display-loop]] :PROPERTIES: :CUSTOM_ID: sec:local-variables :END: Only the [[info:org#Top][Org]] source file shows the local variables footer. # Emacs looks for "Local variables:" after the last "\?n\?f". # Local Variables: # compile-command: "latexmk -interaction=nonstopmode -lualatex -pvc -shell-escape README.tex" # eval: (apply 'my-org-eval-blocks-named '("emacs-lisp-setup")) # fill-column: 80 # End: