#+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 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. 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: Note: src_emacs-lisp[:exports code]{(find-function #'hack-local-variables)} #+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 * [[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 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. 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)) (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 Facilitate installlation of the optional packages. #+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))) #+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 #+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. [[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: