#+title: Emacs setup for use with Go, LaTeX, Lisp, Org, and Python #+author: Gerard Vermeulen #+latex_class: article-local #+latex_class_options: [11pt,a4paper,english,svgnames] #+macro: kbd (eval (by-backend-kbd-org-macro $1)) #+options: ^:{} #+property: header-args:emacs-lisp :exports code :tangle init.el #+startup: showeverything #+begin_src latex :noweb yes :results raw ,#+latex_header: <> ,#+latex_header: <> ,#+latex_header: <> ,#+latex_header: <> ,#+latex_header: <> #+end_src * Copying :PROPERTIES: :CUSTOM_ID: sec:copying :END: This README contains my Emacs setup for use with Go, LaTeX, Lisp, Org, and Python. Copyright \copy 2021-2023 Gerard Vermeulen. #+BEGIN_QUOTE Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". #+END_QUOTE * Quick start :PROPERTIES: :CUSTOM_ID: sec:quick-start :END: This setup requires at least Emacs-29.0.91. Backup the =user-emacs-directory= (defaults often to =~/.emacs.d= on =Linux=, =Unix=, or =Darwin=) and execute the commands in listing [[lst:prepare-user-emacs-directory-with-https][prepare the =user-emacs-directory= without =ssh= access]] or in listing [[lst:prepare-user-emacs-directory-with-ssh][prepare the =user-emacs-directory= with =ssh= access]]. After invoking Emacs interactively (in interactive mode, neither in batch mode, nor in server mode), Emacs will ask you to install a selected set of packages. Quit Emacs and invoke Emacs again. # #+caption[Prepare the user-emacs-directory]: # #+caption: Clone and initialize the user-emacs-directory. # #+name: lst:prepare-user-emacs-directory # #+begin_src shell -n :noeval :tangle no # cd ~ # git clone ccdr@mercury.grenoble.cnrs.fr:SERVER/emacs.d.git .emacs.d # make --directory=.emacs.d init # emacs & # #+end_src #+caption[Prepare the user-emacs-directory without =ssh= access]: #+caption: Clone and initialize the user-emacs-directory without =ssh= access. #+name: lst:prepare-user-emacs-directory-with-https #+begin_src shell -n :noeval :tangle no cd ~ git clone https://forge.chapril.org/gav451/emacs.d.git .emacs.d make --directory=.emacs.d init emacs & #+end_src #+caption[Prepare the =user-emacs-directory= with =ssh= access]: #+caption: Clone and initialize the user-emacs-directory with =ssh= access. #+name: lst:prepare-user-emacs-directory-with-ssh #+begin_src shell -n :noeval :tangle no cd ~ git clone ssh://gitea@forge.chapril.org:222/gav451/emacs.d.git .emacs.d make --directory=.emacs.d init emacs & #+end_src * 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 three methods in my work-flow: 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]] exploits the extension packages [[https://github.com/minad/vertico][Vertico]], [[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]]. A curated repository of CSL styles is the [[https://github.com/citation-style-language/styles#readme][citation style language repository]]. 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 Antolin 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]]. My use =setopt= to set Emacs options instead of the customize interface comes from his idea of using =custom-set-variables= although that is [[https://www.masteringemacs.org/article/bad-emacs-advice][bad Emacs advice]] for new users. 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. 5. [[https://github.com/purcell/emacs.d][Steve Purcell's configuration]] is well organized and a showcase of readable code. Its commit and issue histories are also helpful: see for instance the discussion [[https://github.com/purcell/emacs.d/issues/778][The order of company candidates is incorrect in Emacs lisp mode]]. Here follows a list of links on how to use Emacs and Elisp: 1. [[https://www.masteringemacs.org/][Mastering Emacs]] is a link to a blog with many interesting posts that promotes a book on how to become a proficient Emacs user. 2. [[https://www2.lib.uchicago.edu/keith/emacs/][Use GNU Emacs: The Plain Text Computing Environment]] explains the fundamentals of the Emacs abstractions before showing how to exploit those abstractions interactively. It targets a similar audience as the [[https://www.masteringemacs.org/][Mastering Emacs]] book. 3. [[https://www.youtube.com/watch?v=lkIicfzPBys][A guided tour of Emacs]] is a link to a video tour pointing how [[info:emacs#Buffers][buffers (info)]], [[info:emacs#Dired][dired (info)]], [[info:emacs#Help][documentation (info)]], [[info:elisp#Top][elisp (info)]], [[info:elisp#Debugging][elisp debugging (info)]], [[info:eshell#Top][eshell (info)]], and [[info:emacs#Keyboard Macros][keyboard macros (info)]] turn Emacs in an powerful coding and editing environment. 4. [[https://www.youtube.com/watch?v=6ZWp05OW1c0][Emergency Emacs]] is a link to a video on fundamental editing features and principles with focus on [[info:ediff#Top][ediff (info)]], [[info:emacs#Undo][undo (info)]], [[info:emacs#Moving Point][moving point (info)]], [[info:emacs#Erasing][erasing (info)]], and [[info:emacs#Dynamic Abbrevs][dynamic abbreviations (info)]]. 5. [[https://endlessparentheses.com/][Endless Parentheses]] is a blog with many mindblowing code snippets. 6. [[https://www.murilopereira.com/how-to-open-a-file-in-emacs/][How to open a file in Emacs]] looks into [[info:elisp#Top][elisp (info)]] and [[info:elisp#Debugging][elisp debugging (info)]] and then compares Emacs, [[https://www.vim.org/][Vim]], [[https://neovim.io/][Neovim]], and [[https://code.visualstudio.com/][Visual Studio Code]] with respect to values and technology. 7. [[https://protesilaos.com/codelog/2022-01-31-learning-emacs/][Learning Emacs and Elisp]] is a link to a video tutorial with a transcript on the best approach to learn Emacs and Elisp. * [[info:emacs#Early Init File][Early Init File (info)]] :PROPERTIES: :CUSTOM_ID: sec:early-init-file :END: Try to load [[https://github.com/emacscollective/no-littering][no-littering]] as early as possible, since it helps to keep =~/.emacs.d= clean. #+caption[Tangle the early-init-file]: #+caption: Tangle the early-init-file. #+name: lst:tangle-early-init-file #+begin_src emacs-lisp -n :results silent :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: ;;; early-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, type {{{kbd(C-c C-c)}}} after moving point (or cursor) to one of the items of the list: 1. src_emacs-lisp{(describe-variable #'load-prefer-newer t)} 2. src_emacs-lisp{(apropos-library "no-littering")} 3. src_emacs-lisp{(find-function #'hack-local-variables)} to execute the code between the curly braces for access to help. This shows that *Emacs is a self-documenting editor.* * [[info:emacs#Init File][Init File (info)]] header :PROPERTIES: :CUSTOM_ID: sec:init-file-header :END: The =user-init-file= header requires =cl-lib= and sets Emacs options. It consists of three parts in listing [[lst:1st-setopt-call]], [[lst:2nd-setopt-call]], and [[lst:2nd-setopt-call]] in order to limit the length of the listings for exporting to LaTeX. The [[info:emacs#Init File][init file (info)]] does not load the ~custom-file~ in spite of the recommendation of [[info:emacs#Saving Customizations][saving customizations (info)]]. #+caption[Set the first set of Emacs options]: #+caption: Set the first set of Emacs options. #+name: lst:1st-setopt-call #+begin_src emacs-lisp -n :results silent ;;; init.el --- user init file -*- lexical-binding: t -*- ;;; Commentary: ;;; Code: (require 'cl-lib) (when (version< emacs-version "29.0.91") (error "This `init.el' requires at least Emacs-29.0.91")) (setopt after-save-hook #'executable-make-buffer-file-executable-if-script-p column-number-mode t cursor-type 'box custom-file (locate-user-emacs-file "custom.el") 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")) isearch-lazy-count t kill-ring-max 300 lazy-count-prefix-format nil lazy-count-suffix-format " (%s/%s)" mode-line-compact 'long next-error-message-highlight t recentf-mode t save-place-mode t 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-answers t view-read-only t) #+end_src #+caption[Set the second set of Emacs options: pin packages to archives]: #+caption: Set the second set of Emacs options: pin packages to archives. #+name: lst:2nd-setopt-call #+begin_src emacs-lisp -n :results silent (when (eq system-type 'darwin) (setopt 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))) (setopt ;; See https://github.com/melpa/melpa#mirrors for official Melpa mirrors. ;; ("melpa" . "https://www.mirrorservice.org/sites/melpa.org/packages/") package-archives '(("gnu" . "https://elpa.gnu.org/packages/") ("gnu-devel" . "https://elpa.gnu.org/devel/") ("melpa" . "https://melpa.org/packages/") ("melpa-stable" . "https://stable.melpa.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/")) package-install-upgrade-built-in t ;; Pin packages to GNU ELPA for info for stability. package-pinned-packages '((auctex . "gnu") (compat . "gnu") (consult . "gnu-devel") (embark . "gnu-devel") (embark-consult . "gnu-devel") (eldoc . "nongnu") (engrave-faces . "gnu-devel") (hyperbole . "gnu-devel") (marginalia . "gnu-devel") (osm . "gnu-devel") (parsebib . "melpa-stable") (queue . "gnu") (rainbow-mode . "gnu") (spinner . "gnu") (xr . "gnu") (vertico . "gnu-devel")) package-selected-packages '(async debbugs no-littering)) #+end_src #+caption[Set the third set of Emacs options: upgrade Org and transient]: #+caption: Set the third set of Emacs options: upgrade Org and transient. #+name: lst:3rd-setopt-call #+begin_src emacs-lisp -n :results silent ;; Enable `package-install-upgrade-build-in' to upgrade Org and transient. ;; Disable upgrading other packages by pinning them to "nongnu". (setopt package-install-upgrade-built-in t package-pinned-packages (cl-union '((bind-key . "nongnu") (eglot . "nongnu") (erc . "nongnu") (external-completion . "nongnu") (faceup . "nongnu") (jsonrpc . "nongnu") (let-alist . "nongnu") (map . "nongnu") (ntlm . "nongnu") (org . "gnu-devel") ; Upgrade! (python . "nongnu") (project . "nongnu") (seq . "nongnu") (soap-client . "nongnu") (so-long . "nongnu") (svg . "nongnu") (transient . "melpa") ; Upgrade! (use-package . "nongnu") (use-package-ensure-system-package . "nongnu") (verilog-mode . "nongnu") (xref . "nongnu")) package-pinned-packages :key #'car)) #+end_src #+caption[Handle ~user-emacs-directory~ site-lisp directories as packages]: #+caption: Handle ~user-emacs-directory~ site-lisp directories as packages. #+name: lst:site-lisp-packages #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; https://www.emacswiki.org/emacs/LoadPath ;; Make user-emacs-directory site-lisp directories look like packages. (mapc (apply-partially 'add-to-list 'load-path) (cl-remove-duplicates (mapcar (lambda (file) (directory-file-name (file-name-directory file))) (directory-files-recursively (expand-file-name "site-lisp" user-emacs-directory) "\\.el$")) :test #'string=)) ;; See package.el for how to use `loaddefs-generate-dir'. (defun make-loaddefs-in-buffer-directory () "Make or update the autoloads in the directory of the visited file." (interactive) (let ((dir (directory-file-name (file-name-directory (buffer-file-name)))) (name (file-name-base (directory-file-name (file-name-directory (buffer-file-name)))))) (loaddefs-generate dir (expand-file-name (format "%s-autoloads.el" name) dir) nil nil nil 'generate-full)))) #+end_src * [[info:emacs#Package Installation][Install the selected packages (info)]] :PROPERTIES: :CUSTOM_ID: sec:install-selected-packages :END: [[info:emacs#Package Installation][Emacs installs packages]] from archives on the internet. This setup uses five archives: 1. The [[https://elpa.gnu.org/][GNU Emacs Lisp Package Archive]]. 2. The [[https://elpa.gnu.org/devel/index.html][Development Emacs Lisp Package Archive]]. 3. The [[https://elpa.nongnu.org/][NonGNU Emacs Lisp Package Archive]]. 4. The [[https://melpa.org/#/][Milkypostman's Emacs Lisp Package Archive (MELPA)]] with its official [[https://www.mirrorservice.org/sites/melpa.org/packages/][mirror]] in case [[https://downforeveryoneorjustme.com/melpa.org][is Melpa.org down?]] tells [[https://melpa.org/#/][MELPA]] is down for everyone. 5. The [[https://stable.melpa.org/#/getting-started][Stable Milkypostman's Emacs Lisp Package Archive]]. The code in listing [[lst:install-selected-packages]] assumes that the package system is in a *virgin* state if the package [[https://github.com/emacscollective/no-littering][no-littering]] is not present: 1. It installs and loads [[https://github.com/emacscollective/no-littering][no-littering]] after ensuring refreshing of the contents of available packages. 2. It upgrades the build in Org package. 3. It calls src_emacs-lisp{(package-install-selected-packages)} to check the installation status of all packages in src_emacs-lisp{package-selected-packages} and to install the missing packages after the user has agreed to its prompt. 4. It defines a function to ensure the installation of packages in other source blocks. This allows skipping installation in sections with a =:noexport:= tag by disallowing tangling. In case of normal Emacs usage, src_emacs-lisp{(package-list-packages)} refreshes the contents of packages and allows to update packages to the latest version. #+caption[Install the selected packages]: #+caption: Install the selected packages. #+name: lst:install-selected-packages #+begin_src emacs-lisp -n :results silent (unless noninteractive (unless (require 'no-littering nil 'noerror) (unless (bound-and-true-p package-archive-contents) (package-refresh-contents)) ;; Install and require `no-littering'. (package-install 'no-littering) (require 'no-littering) (package-upgrade 'org) ;; Install the selected packages. (package-install-selected-packages))) (defun ensure-package-selection (package) "Ensure selection of PACKAGE." (when (and (package-installed-p package) (bound-and-true-p package-selected-packages)) (cl-pushnew package package-selected-packages))) (defun ensure-package-installation (&rest packages) "Ensure installation of all packages in PACKAGES." (let ((ok t)) (dolist (package packages) (unless (package-installed-p package) (package-install package)) (unless (ensure-package-selection package) (setq ok nil))) ok)) #+end_src * [[https://git.savannah.gnu.org/cgit/emacs.git/tree/admin/notes/tree-sitter/starter-guide?h=feature/tree-sitter][Emacs Tree-sitter]] :PROPERTIES: :CUSTOM_ID: sec:emacs-tree-sitter :END: Mickey Peterson's post [[https://www.masteringemacs.org/article/how-to-get-started-tree-sitter?utm_source=newsletter&utm_medium=rss][How To Get Started with Tree-Sitter]] explains how to use =tree-sitter= in Emacs-29.0.91. Listing [[lst:setup-treesit-sources][Setup =treesit= grammar sources]] configures where to find the different =tree-sitter= grammar sources for different languages. Listing [[lst:setup-go][Setup Go programming]] shows how to configure ~tab-width~ for ~go-ts-mod~ and ~go-mode-ts-mode~. #+caption[Setup =treesit= grammar sources]: #+caption: Setup =treesit= grammar sources. #+name: lst:setup-treesit-sources #+begin_src emacs-lisp -n :results silent (when (require 'treesit nil 'noerror) (setopt treesit-language-source-alist '((bash "https://github.com/tree-sitter/tree-sitter-bash" "master" "src") (c "https://github.com/tree-sitter/tree-sitter-c" "master" "src") (cmake "https://github.com/uyha/tree-sitter-cmake" "master" "src") (cpp "https://github.com/tree-sitter/tree-sitter-cpp" "master" "src") (css "https://github.com/tree-sitter/tree-sitter-css" "master" "src") (glsl "https://github.com/theHamsta/tree-sitter-glsl" "master" "src") ;; go-ts-mode requires libtree-sitter-go and libtree-sitter-gomode. (go "https://github.com/tree-sitter/tree-sitter-go" "master" "src") (gomod "https://github.com/camdencheek/tree-sitter-go-mod" "main" "src") (html "https://github.com/tree-sitter/tree-sitter-html" "master" "src") (java "https://github.com/tree-sitter/tree-sitter-java" "master" "src") (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src") (json "https://github.com/tree-sitter/tree-sitter-json" "master" "src") (julia "https://github.com/tree-sitter/tree-sitter-julia" "master" "src") (make "https://github.com/alemuller/tree-sitter-make" "main" "src") (perl "https://github.com/ganezdragon/tree-sitter-perl" "master" "src") (python "https://github.com/tree-sitter/tree-sitter-python" "master" "src") (ruby "https://github.com/tree-sitter/tree-sitter-ruby" "master" "src") (rust "https://github.com/tree-sitter/tree-sitter-rust" "master" "src") (scala "https://github.com/tree-sitter/tree-sitter-scala" "master" "src") (sql "https://github.com/m-novikov/tree-sitter-sql" "main" "src") (swift "https://github.com/tree-sitter/tree-sitter-swift" "master" "src") (toml "https://github.com/tree-sitter/tree-sitter-toml" "master" "src") (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src") (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src") (yaml "https://github.com/ikatyang/tree-sitter-yaml" "master" "src")))) #+end_src * [[info:emacs#Faces][Text faces or styles (info)]] :PROPERTIES: :CUSTOM_ID: sec:text-faces-or-styles :END: This setup does not use [[info:emacs#Custom Themes][custom themes (info)]] to avoid endless tweaking, but it does a minimal setup of fonts and faces. 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 code in listing [[lst:configure-face-attributes]] source implements those rules. Listing [[lst:set-default-face-height]] shows that font scaling is easy in case of proper initialization of all face heigths. Listing [[lst:fix-gtk-color-for-invert-default-face]], [[lst:shadow-org-font-lock-faces]], and [[lst:shadow-org-font-lock-faces]] show a few tweaks to improve visibility without theming: 1. Fixing the =gtk= background color of the already loaded =region= face. 2. Toggling between a dark and light background by means of src_emacs-lisp{(invert-default-face)}. 3. Shadowing the definition of faces before loading. The last item in the page on [[https://orgmode.org/worg/org-contrib/babel/examples/fontify-src-code-blocks.html#org5c4406f][fontifying Org mode source code blocks]] describes this method. #+caption[Configure =face-attributes=]: #+caption: Configure =face-attributes=. #+name: lst:configure-face-attributes #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; 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 #+caption[Implement =set-default-face-height=]: #+caption: Implement =set-default-face-height=. #+name: lst:set-default-face-height #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (defun 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))) (choices (mapcar #'number-to-string (number-sequence 50 200 10))) (height (string-to-number (completing-read prompt choices nil 'require-match)))) (message "Setting the height of the default face to %s" height) (set-face-attribute 'default nil :height height)))) #+end_src #+caption[Use =buffer-face-mode= to set fixed or variable pitch face]: #+caption: Use =buffer-face-mode= to set fixed or variable pitch face. #+name: lst:use-buffer-face-mode-for-fixed-or-variable-pitch-face #+begin_src emacs-lisp -n :results silent ;; Use proportional font faces in current buffer (defun set-buffer-variable-pitch-face () "Set a variable width (proportional) font in current buffer." (interactive) (setq buffer-face-mode-face 'variable-pitch) (buffer-face-mode)) ;; Use monospaced font faces in current buffer (defun set-buffer-fixed-pitch-face () "Set a fixed width (monospace) font in current buffer." (interactive) (setq buffer-face-mode-face 'fixed-pitch) (buffer-face-mode)) (add-hook 'magit-mode-hook #'set-buffer-fixed-pitch-face) (add-hook 'prog-mode-hook #'set-buffer-fixed-pitch-face) #+end_src #+caption[Fix a `gtk' color and implement =invert-default-face=]: #+caption: Fix a `gtk' color and implement =invert-default-face=. #+name: lst:fix-gtk-color-for-invert-default-face #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (defun fix-gtk-region-face-background-color () (when (featurep 'gtk) (set-face-attribute 'region nil :background (cdr (assoc (face-attribute 'default :background) '(("white" . "LightGoldenrod2") ("black" . "blue3"))))))) (defun invert-default-face () "Invert the default face." (interactive) (invert-face 'default) (fix-gtk-region-face-background-color)) (fix-gtk-region-face-background-color)) #+end_src #+caption[Shadow Org font-lock faces to improve the readability]: #+caption: Shadow Org font-lock faces to improve the readability. #+name: lst:shadow-org-font-lock-faces #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; Shadow two definitions in org-faces.el: (defface org-block ;; https://emacs.stackexchange.com/a/9604 answers: ;; How to override a defined face for light and dark backgrounds? `((((background dark)) :inherit (fixed-pitch) ,@(and (>= emacs-major-version 27) '(:extend t)) :background "#444444") (t :inherit (fixed-pitch) ,@(and (>= emacs-major-version 27) '(:extend t)) :background "#FFFFD0")) "My face used for text inside various blocks. It is always used for source blocks. You can refine what face should be used depending on the source block language by setting, `org-src-block-faces', which takes precedence. When `org-fontify-quote-and-verse-blocks' is not nil, text inside verse and quote blocks are fontified using the `org-verse' and `org-quote' faces, which inherit from `org-block'.") (defface org-table ;; https://emacs.stackexchange.com/a/9604 answers: ;; How to override a defined face for light and dark backgrounds? `((((background dark)) :inherit (fixed-pitch) ,@(and (>= emacs-major-version 27) '(:extend t)) :background "#444444") (t :inherit (fixed-pitch) ,@(and (>= emacs-major-version 27) '(:extend t)) :background "#FFFFD0")) "My face for tables.")) #+end_src #+caption[Shadow Emacs font-lock faces to improve the readability]: #+caption: Shadow Emacs font-lock faces to improve the readability. #+name: lst:shadow-emacs-font-lock-faces #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; Shadow one definition in sh-script.el: (defface sh-heredoc '((((class color) (background dark)) (:foreground "yellow")) (((class color) (background light)) (:foreground "magenta")) (t (:weight bold))) "My face to show a here-document.")) #+end_src * [[info:emacs#Windows][Window management (info)]] :PROPERTIES: :CUSTOM_ID: sec:window-management :END: Mickey Peterson's post [[https://www.masteringemacs.org/article/demystifying-emacs-window-manager][Demystifying Emacs's Window Manager]] invites to improve window placement. Listing [[lst:1st-window-management]], [[lst:2nd-window-management]] and [[lst:3rd-window-management]] implement a selection of his recommendations. #+caption[Window management functions and key bindings]: #+caption: Window management functions and key bindings. #+name: lst:1st-window-management #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; https://www.masteringemacs.org/article/demystifying-emacs-window-manager (defun split-root-below (arg) "Split window below from the root or from the parent with ARG." (interactive "P") (split-window (if arg (window-parent (selected-window)) (frame-root-window)) nil 'below nil)) (defun split-root-right (arg) "Split window right from the root or from the parent with ARG." (interactive "P") (split-window (if arg (window-parent (selected-window)) (frame-root-window)) nil 'right nil)) (defun toggle-window-dedication () "Toggles window dedication in the selected window." (interactive) (set-window-dedicated-p (selected-window) (not (window-dedicated-p (selected-window))))) (defun make-display-buffer-matcher-function (major-modes) "Return a lambda function to match a list of MAJOR-MODES." (lambda (buffer-name action) (with-current-buffer buffer-name (apply #'derived-mode-p major-modes)))) (keymap-global-set "M-o" #'other-window)) #+end_src #+caption[Window management modes]: #+caption: Window management modes. #+name: lst:2nd-window-management #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (winner-mode +1)) #+end_src #+caption[Window management variables]: #+caption: Window management variables. #+name: lst:3rd-window-management #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; https://www.masteringemacs.org/article/demystifying-emacs-window-manager (add-to-list 'display-buffer-alist `(,(rx (or "*Apropos*" "*Help*" "*info")) (display-buffer-reuse-window display-buffer-pop-up-window) (inhibit-same-window . nil))) (add-to-list 'display-buffer-alist `(,(rx (or "*Occur*" "*grep*" "*xref*")) display-buffer-reuse-window (inhibit-same-window . nil))) (add-to-list 'display-buffer-alist '("\\*compilation\\*" display-buffer-no-window (allow-no-window . t))) (add-to-list 'display-buffer-alist '("^\\*Dictionary\\*" display-buffer-in-side-window (side . left) (window-width . 70)))) #+end_src * [[info:elisp#Advising Functions][Advising functions (info)]] :PROPERTIES: :CUSTOM_ID: sec:advising-function :END: #+caption[Toggle any advice]: #+caption: Toggle any advice. #+name: lst:toggle-any-advice #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (defun advice-toggle (symbol where function &optional props) "Toggle between states after `advice-remove' and `advice-add'." (let ((how "%s `%s' advice `%s' %s `%s'")) (if (advice-member-p function symbol) (progn (message how "Removal of" where function "from" symbol) (advice-remove symbol function)) (message how "Addition of" where function "to" symbol) (advice-add symbol where function props))))) #+end_src #+caption[Toggle specific advice]: #+caption: Toggle specific advice. #+name: lst:toggle-specific-advice #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (defun toggle-eww-display-pdf-around () "Toggle `eww-display-advice' advice." (interactive) (advice-toggle 'eww-display-pdf :around #'eww-display-pdf-as-binary)) (defun toggle-ilog-timer-function-after () "Toggle `ilog-timer-function' advice." (interactive) (advice-toggle 'ilog-timer-function :after #'ilog-ensure-ilog-buffer-window)) (defun toggle-keycast-log-update-buffer-override () "Toggle `keycast-log-update-buffer' advice." (interactive) (advice-toggle 'keycast-log-update-buffer :override #'keycast-log-update-buffer-plain)) (defun toggle-org-babel-python-format-session-value-override () "Toggle `org-babel-python-format-session-value' advice." (interactive) (advice-toggle 'org-babel-python-format-session-value :override #'org-babel-python-format-session-value-override))) #+end_src * [[info:emacs#Dired][Dired: directory editor as file manager (info)]] :PROPERTIES: :CUSTOM_ID: sec:file-manager :END: [[info:emacs#Dired][Dired (info)]] and the [[https://github.com/ranger/ranger#readme][ranger]] file manager offer similar capabilities for copying, deleting, opening, renaming, and viewing files and directories, but the integration of [[info:emacs#Dired][dired (info)]] in Emacs is obviously much better than the integration of [[https://github.com/ranger/ranger#readme][ranger]] in Emacs. For instance, this setup allows to insert an =org-mode= link to an poorly identified file containing a specific image into an =org-mode= buffer by means of the facilities of [[info:emacs#Dired][dired (info)]] as follows: 1. Type {{{kbd(C-x d)}}} to open the directory containing the pooly identified file with the specific image in a =dired-mode= buffer. 2. Start searching for the specific image by navigating to the name of any image file. 3. Type {{{kbd(v)}}} to switch to view the file in an =image-mode= buffer. 4. In the =image-mode= buffer, continue searching for the specific image by typing: - {{{kbd(n)}}} to view the next image in the =dired-mode= buffer, - {{{kbd(p)}}} to view the previous image in the =dired-mode= buffer, and - {{{kbd(q)}}} to quit the =image-mode= buffer and to go back to the =dired-mode= buffer. 5. After finding the specific image, use {{{kbd(C-c l)}}} in the =image-mode= buffer or the =dired-mode= buffer to store the =org-link=. 6. Switch to the =org-mode= buffer and use {{{kbd(C-c C-l)}}} to insert the =org-link= into the buffer. Listing [[lst:set-dired-options]] makes the directory editor sort entries alphabetically after grouping the directories before grouping the files by extension. #+caption[Set =dired= options]: #+caption: Set =dired= options. #+name: lst:set-dired-options #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'dired (setopt dired-dwim-target t ;; | switch | action | ;; |--------+----------------------------------------| ;; | -a | also list hidden entries | ;; | -l | use a long listing format | ;; | -X | sort alphabetically by entry extension | ;; | -G | skip long listing format group names | ;; | -1 | list one entry per line | dired-listing-switches "-alGX1 --group-directories-first" dired-recursive-copies 'always dired-recursive-deletes 'always)) (with-eval-after-load 'files ;; Ensure the use of `GNU-ls' from `coreutils' on darwin. (setopt insert-directory-program (or (executable-find "gls") (executable-find "ls")))) (with-eval-after-load 'wdired (setopt wdired-allow-to-change-permissions t)) #+end_src Listing [[lst:extra-dired-key-bindings]] adds new key bindings to =dired-mode-map= to: 1. Open files with the [[https://en.wikipedia.org/wiki/Eww_(web_browser)][Emacs Web Wowser]] inside Emacs. 2. Let [[https://en.wikipedia.org/wiki/Rsync][rsync]] copy marked files outside Emacs to a local or remote directory. The link [[https://truongtx.me/tmtxt-dired-async.html][asynchoronous execution library for Emacs Dired]] is the original source for the idea of using [[https://en.wikipedia.org/wiki/Rsync][rsync]] and the blog articles [[https://oremacs.com/2016/02/24/dired-rsync/][using rsync in dired]] and [[https://vxlabs.com/2018/03/30/asynchronous-rsync-with-emacs-dired-and-tramp/][asynchronous rsync with Emacs, dired and tramp]] are vivid recommendations written by experienced Emacs users. #+caption[Extra =dired= key bindings]: #+caption: Extra =dired= key bindings. #+name: lst:extra-dired-key-bindings #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'dired (defun dired-eww-open-file () "In Dired, open the regular file named on this line with eww" (interactive) (let ((file (dired-get-file-for-visit))) (if (file-regular-p file) (eww-open-file file) (error "Eww rejects `%s', since it is not a regular file" file)))) ;; https://truongtx.me/tmtxt-dired-async.html (defun dired-rsync (target) "Copy marked files with `rsync' to TARGET directory." (interactive (list (expand-file-name (read-file-name "Rsync to:" (dired-dwim-target-directory))))) ;; Store all marked files into the `files' list and intialize ;; the `rsync-command'. (let ((files (dired-get-marked-files nil current-prefix-arg)) (rsync-command "rsync -av --progress ")) ;; Add all marked files as arguments to the `rsync-command'. (dolist (file files) (setq rsync-command (concat rsync-command (if (string-match "^/ssh:\\(.*\\)$" file) (format " -e ssh %s" (match-string 1 file)) (shell-quote-argument file)) " "))) ;; Append the destination directory to the `rsync-command'. (setq rsync-command (concat rsync-command (if (string-match "^/ssh:\\(.*\\)$" target) (format " -e ssh %s" (match-string 1 target)) (shell-quote-argument target)))) ;; Run the async shell command. (async-shell-command rsync-command) ;; Finally, switch to that window. (other-window 1))) (keymap-set dired-mode-map "E" #'dired-eww-open-file) (keymap-set dired-mode-map "Y" #'dired-rsync)) #+end_src * [[info:emacs#Completion Styles][Minibuffer completion styles (info)]] :PROPERTIES: :CUSTOM_ID: sec:minibuffer-completion-styles :END: Listing [[lst:set-minibuffer-options][set minibuffer options]] implements ideas of the post [[https://www.masteringemacs.org/article/understanding-minibuffer-completion][Understanding minibuffer completion]]. #+caption[Set =minibuffer= options]: #+caption: Set =minibuffer= options. #+name: lst:set-minibuffer-options #+begin_src emacs-lisp -n :results silent (when (require 'minibuffer nil 'noerror) ;; https://www.masteringemacs.org/article/understanding-minibuffer-completion (setopt completion-category-overrides '((file (styles basic substring))) completion-ignore-case nil completion-styles '(basic flex partial-completion substring))) #+end_src * [[info:elisp#Processes][Processes (info)]] :PROPERTIES: :CUSTOM_ID: sec:processes :END: Listing [[lst:process-utilities]] defines a function to run a (command-line) program with arguments and to obtain a list with its numeric exit status as well as its output to =stdout=. #+caption[Process utilities]: #+caption: Process utilities. #+name: lst:process-utilities #+begin_src emacs-lisp -n :results silent ;; https://gitlab.com/howardabrams/spacemacs.d/blob/master/layers/ha-org/funcs.el#L418 (defun shell-command-with-exit-code (program &rest args) "Run PROGRAM with ARGS and return exit-code and output in a list." (with-temp-buffer (list (apply 'call-process program nil (current-buffer) nil args) (buffer-substring-no-properties (point-min) (point-max))))) #+end_src * [[info:emacs#Help][Help (info)]] :PROPERTIES: :CUSTOM_ID: sec:help :END: Table [[tab:help-key-bindings]] lists a number of key bindings to start playing with the help facilities of Emacs. Try {{{kbd(C-h o)}}} instead of {{{kbd(C-h f)}}} or {{{kbd(C-h v)}}}. #+attr_latex: :booktabs yes :float table #+caption[Help key bindings]: #+caption: Help key bindings. #+name: tab:help-key-bindings |------------------------------+----------+------------------| | command | key map | keys | |------------------------------+----------+------------------| | Info-goto-emacs-command-node | help-map | {{{kbd(C-h F)}}} | | describe-function | help-map | {{{kbd(C-h f)}}} | | describe-key | help-map | {{{kbd(C-h k)}}} | | describe-symbol | help-map | {{{kbd(C-h o)}}} | | describe-variable | help-map | {{{kbd(C-h v)}}} | | info | help-map | {{{kbd(C-h i)}}} | |------------------------------+----------+------------------| #+begin_src emacs-lisp -n :exports none :results silent :tangle no (defun org-key-binding-macro (cmd) (let* ((sym (intern-soft cmd)) (keys (and (commandp sym) (where-is-internal sym nil 'first-only))) (prefix (seq-take keys (1- (length keys)))) (keymap (key-binding prefix 'accept-default))) (format "%s %s %s {{{kbd(%s)}}}" cmd keymap (help--binding-locus keys nil) (key-description keys)))) (org-key-binding-macro 'consult-buffer) #+end_src ** [[info:emacs#Name Help][Shortdoc-display-group (info)]] :PROPERTIES: :CUSTOM_ID: sec:shortdoc-display-group :END: Listing [[lst:configure-shortdoc]] binds {{{kbd(C-h y)}}} to =short-doc-display-group= and defines a short documentation group for functions defined in this Org file. #+caption[Configure =shortdoc=]: #+caption: Configure =shortdoc=. #+name: lst:configure-shortdoc #+begin_src emacs-lisp -n :results silent (when (fboundp 'shortdoc-display-group) (keymap-set help-map "y" #'shortdoc-display-group) (with-eval-after-load 'shortdoc ;; Ensure defining the functions before documenting them. (define-short-documentation-group init "Advice" (advice-toggle :no-manual t) (toggle-eww-display-pdf-around :no-manual t) (toggle-ilog-timer-function-after :no-manual t) (toggle-keycast-log-update-buffer-override :no-manual t) (toggle-org-babel-python-format-session-value-override :no-manual t) "Face" (invert-default-face :no-manual t) (set-default-face-height :no-manual t) "Interaction" (enable-this-command :no-manual t) (narrow-or-widen-dwim :no-manual t) (org-narrow-to-table :no-manual t) "LaTeX" (biber-delete-cache :no-manual t) (update-lualatex-opentype-font-name-database :no-manual t) "Org" (by-backend :no-manual t) (by-backend-kbd-org-macro :no-manual t) (org-active-current-time-stamp :nomanual t) (org-babel-execute:latex-extra-header :no-manual t) (org-babel-execute:latex-header :no-manual t) (org-electric-dollar :no-manual t) (org-eval-emacs-lisp-setup-blocks :no-manual t) (org-eval-python-setup-blocks :no-manual t) (org-eval-infixed-blocks :no-manual t) (org-latex-engraved-source-block-filter :no-manual t) (org-inactive-current-time-stamp :nomanual t) (org-syntax-convert-keyword-case-to-lower :no-manual t) (smart-latex-engrave-org-source-blocks :no-manual t) (zero-all-org-src-blocks-indentation :no-manual t) "Python" (choose-common-python-interpreter :no-manual t) (choose-common-python-linter :no-manual t)))) #+end_src ** [[info:info#Top][Info (info)]] :PROPERTIES: :CUSTOM_ID: sec:info :END: Listing [[lst:configure-info]] adds the required paths to the places where =info= looks for files. #+caption[Configure =info=]: #+caption: Configure =info=. #+name: lst:configure-info #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'info ;; Make Emacs find ALL "*.info" files in `package-user-dir' on Gentoo Linux. (when (eq system-type 'gnu/linux) (dolist (path (nreverse (mapcar (lambda (name) (expand-file-name (file-name-directory name))) (directory-files-recursively package-user-dir "\\.info\\'")))) (add-to-list 'Info-directory-list path nil #'file-equal-p))) ;; Make Emacs find my "python.info" file. (add-to-list 'Info-directory-list (expand-file-name "~/.local/share/info"))) #+end_src * [[info:emacs#Key Bindings][Key bindings (info)]] :PROPERTIES: :CUSTOM_ID: sec:key-bindings :END: #+attr_latex: :booktabs yes :float table #+caption[Basic key bindings]: #+caption: Basic key bindings. #+name: tab:basic-key-bindings |--------------------+------------+------------------------| | command | key map | keys | |--------------------+------------+------------------------| | undo | global-map | {{{kbd(s-z)}}} | | backward-kill-word | global-map | {{{kbd(C-backspace)}}} | | backward-char | global-map | {{{kbd(C-b)}}} | | forward-char | global-map | {{{kbd(C-f)}}} | | backward-word | global-map | {{{kbd(M-b)}}} | | forward-word | global-map | {{{kbd(M-f)}}} | | backward-sentence | global-map | {{{kbd(M-a)}}} | | forward-sentence | global-map | {{{kbd(M-e)}}} | |--------------------+------------+------------------------| ** [[info:emacs#Disabling][Disabling Commands (info)]] :PROPERTIES: :CUSTOM_ID: sec:enable-disabled-commands :END: Execute src_emacs-lisp{(find-library "novice")} to see how Emacs prevents new users from shooting themselves in the feet. Listing [[lst:configure-disabled-command-function]] enables [[https://www.emacswiki.org/emacs/DisabledCommands][disabled commands on the fly]]. #+caption[Configure the =disabled-command-function=]: #+caption: Configure the =disabled-command-function=. #+name: lst:configure-disabled-command-function #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (setq disabled-command-function (defun 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 ** [[https://github.com/michael-heerdegen/interaction-log.el#readme][Interaction-log]] :PROPERTIES: :CUSTOM_ID: sec:interaction-log :END: #+caption[Configure =interaction-log=]: #+caption: Configure =interaction-log=. #+name: lst:configure-interaction-log #+begin_src emacs-lisp -n :results silent (when (and (ensure-package-installation 'interaction-log) (require 'interaction-log nil 'noerror)) ;; https://www.skybert.net/emacs/show-shortcuts-when-giving-presentations/ (defun ilog-ensure-ilog-buffer-window () "Ensure the visibility of the `ilog' buffer, when it exists." (when (and (get-buffer ilog-buffer-name) (not (get-buffer-window ilog-buffer-name))) (display-buffer (get-buffer ilog-buffer-name)))) (keymap-set help-map "C-l" #'interaction-log-mode) (advice-add 'ilog-timer-function :after #'ilog-ensure-ilog-buffer-window)) #+end_src ** [[https://github.com/tarsius/keycast#readme][Keycast]] :noexport: :PROPERTIES: :CUSTOM_ID: sec:keycast :header-args:emacs-lisp: :tangle no :eval never-export :END: Listing [[lst:configure-keycast]] configures =keycast=. #+caption[Configure =keycast=]: #+caption: Configure =keycast=. #+name: lst:configure-keycast #+begin_src emacs-lisp -n :results silent ;; Make `keycast-log-update-buffer' use a buffer similar to the ;; control buffer `ediff-setup-windows-plain' returns. (when (and (ensure-package-installation 'keycast) (require 'keycast nil 'noerror)) (setopt keycast-mode-line-window-predicate 'keycast-bottom-right-window-p) (defun keycast-log-update-buffer-plain () (let ((buffer (get-buffer keycast-log-buffer-name))) (unless (buffer-live-p buffer) (setq buffer (get-buffer-create keycast-log-buffer-name)) (with-current-buffer buffer (setq buffer-read-only t))) (unless (get-buffer-window buffer) (display-buffer buffer '(display-buffer-at-bottom (dedicated . t) (window-height . 10)))) (when-let ((output (keycast--format keycast-log-format))) (with-current-buffer buffer (goto-char (if keycast-log-newest-first (point-min) (point-max))) (let ((inhibit-read-only t)) (when (and (> keycast--command-repetitions 0) (string-match-p "%[rR]" keycast-log-format)) (unless keycast-log-newest-first (backward-char)) (ignore-errors (delete-region (line-beginning-position) (1+ (line-end-position))))) (insert output)) (goto-char (if keycast-log-newest-first (point-min) (point-max))))))) (advice-add 'keycast-log-update-buffer :override #'keycast-log-update-buffer-plain)) #+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=. Section [[#sec:latexmk-save-compile-display-loop]] and [[#sec:qutebrowser-userscript]] 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. The code in listing [[lst:start-emacs-server]] starts the Emacs server. #+caption[Start the Emacs server]: #+caption: Start the Emacs server. #+name: lst:start-emacs-server #+begin_src emacs-lisp -n :results silent (when window-system (unless (or noninteractive (daemonp)) (add-hook 'after-init-hook #'server-start))) #+end_src ** Latexmk save-compile-display loop :PROPERTIES: :CUSTOM_ID: sec:latexmk-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 successful (re)compilation on condition that the local ~compile-command~ variable is correct. The local ~compile-command~ variable in the [[#sec:local-variables][local variables]] section (only visible in =org= files, but not in =html= and =pdf= files) shows how to make use of the =latexmkrc= file. #+attr_latex: :options breaklines #+caption[Tangle the =Latexmk= resource file]: #+caption: Tangle the =Latexmk= resource file. #+name: lst:latexmkrc #+begin_src perl -n :tangle latexmkrc :comments no # pdf creator $pdf_mode = 4; # 4 means lualatex # pdf previewer and update pdf previewer $pdf_previewer = "emacsclient -e '(find-file-other-window %S)'"; $pdf_update_method = 4; # 4 runs $pdf_update_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' ); # use ".=" to append to $clean_ext $clean_ext .= " acr acn alg bbl dvi glo gls glg ist lol lst"; $clean_ext .= " nav run.xml snm synctex.gz"; sub makeglossaries { my ($name, $path) = fileparse( $$Psource ); return system "makeglossaries -d '$path' '$name'"; } # Emacs looks for "Local variables:" after the last "newline-formfeed". # 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. #+caption[Tangle a qutebrowser userscript]: #+caption: Tangle a qutebrowser userscript. #+header: :comments no #+header: :tangle-mode (identity #o755) #+name: lst:qutebrowser-userscript #+begin_src python -n :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 ** TODO Look into: org-protocol handling with other browser on Darwin :PROPERTIES: :CUSTOM_ID: sec:org-protocol-darwin :END: 1. [[http://rwx.io/posts/osx-uri-protocol-handler/][Patrick Goddi: macOS URI protocol handler]] and his [[https://github.com/fooqri/uri-handler][URI-Handler]]. 2. [[https://www.hammerspoon.org/][Hammerspoon]] 3. [[https://vritser.github.io/posts/capture-anywhere/][Emacs Capture Anywhere]] ** [[https://en.wikipedia.org/wiki/AppleScript][AppleScript]] :noexport: :PROPERTIES: :CUSTOM_ID: sec:applescript :header-args:emacs-lisp: :tangle no :END: #+caption[Ensure =applescript-mode= installation]: #+caption: Ensure =applescript-mode= installation. #+name: lst:ensure-applescript-mode-installation #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'applescript-mode)) #+end_src #+caption[Applescript code to register =org-protocol= on Darwin]: #+caption: Applescript code to register =org-protocol= on Darwin. #+name: lst:applescript-org-protocol #+begin_src applescript :tangle no on emacsclient(input) do shell script "/usr/local/bin/emacsclient -n -c '" & input & "'" tell application "Emacs" to activate end emacsclient on open location input emacsclient(input) end open location on open inputs repeat with raw_input in inputs set input to POSIX path of raw_input emacsclient(input) end repeat end open on run do shell script emacsclient("") end run #+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:embark#Top][Embark (info)]] for minibuffer actions with context menus, 2. [[info:marginalia#Top][Marginalia (info)]] for rich annotations in the minibuffer, and 3. [[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]] provides completion in any buffer. ** [[info:company#Top][Company (info)]] :PROPERTIES: :CUSTOM_ID: sec:company :END: [[info:company#Top][Company (info)]] is a modular completion framework and the the snippet [[lst:ensure-company-installation][below]] ensures its installation. #+caption[Ensure =company= installation]: #+caption: Ensure =company= installation. #+name: lst:ensure-company-installation #+begin_src emacs-lisp -n :results silent (ensure-package-installation 'company) #+end_src ** [[info:vertico#Top][Vertico (info)]] :PROPERTIES: :CUSTOM_ID: sec:vertico-configuration :END: Listing [[lst:enable-vertico-mode]] configures and enables =savehist-mode= and enables =vertico-mode=. The documentation src_emacs-lisp{(describe-function 'savehist-mode)} why it is best to turn on =savehist-mode= in the Emacs init file. #+caption[Enable =savehist-mode= and =vertico-mode=]: #+caption: Enable =savehist-mode= and =vertico-mode=. #+name: lst:enable-vertico-mode #+begin_src emacs-lisp -n :results silent (when (require 'savehist nil 'noerror) (setopt savehist-additional-variables '(eww-history kill-ring regexp-search-string search-ring search-string)) (savehist-mode +1)) (when (and (ensure-package-installation 'vertico) (fboundp 'vertico-mode)) (vertico-mode +1)) (with-eval-after-load 'vertico (keymap-set vertico-map "RET" #'vertico-directory-enter) (keymap-set vertico-map "DEL" #'vertico-directory-delete-char) (keymap-set vertico-map "M-DEL" #'vertico-directory-delete-word)) #+end_src #+attr_latex: :booktabs yes :float table #+caption[Vertico key map bindings]: #+caption: Vertico key map bindings. #+name: tab:vertico-keymap-bindings |-------------------------------+----------------------------------+---------------------| | command | remap | keys | |-------------------------------+----------------------------------+---------------------| | vertico-directory-delete-char | | {{{kbd(DEL)}}} | | vertico-directory-delete-word | | {{{kbd(M-DEL)}}} | | vertico-directory-enter | | {{{kbd(RET)}}} | | vertico-exit | exit-minibuffer | {{{kbd(C-j)}}} | | vertico-exit-input | | {{{kbd(C-RET)}}} | | vertico-first | beginning-of-buffer | {{{kbd(M-<)}}} | | vertico-first | minibuffer-beginning-of-buffer | {{{kbd(M-<)}}} | | vertico-insert | | {{{kbd(TAB)}}} | | vertico-last | end-of-buffer | {{{kbd(M->)}}} | | vertico-next-group | forward-paragraph | {{{kbd(C-)}}} | | vertico-next | next-line-or-history-element | {{{kbd()}}} | | vertico-next | next-line | {{{kbd(C-n)}}} | | vertico-previous-group | backward-paragraph | {{{kbd(C-)}}} | | vertico-previous | previous-line-or-history-element | {{{kbd()}}} | | vertico-previous | previous-line | {{{kbd(C-p)}}} | | vertico-save | kill-ring-save | {{{kbd(M-w)}}} | | vertico-scroll-down | scroll-down-command | {{{kbd(M-v)}}} | | vertico-scroll-up | scroll-up-command | {{{kbd(C-v)}}} | |-------------------------------+----------------------------------+---------------------| ** [[info:orderless#Top][Orderless (info)]] :noexport: :PROPERTIES: :CUSTOM_ID: sec:orderless-configuration :header-args:emacs-lisp: :tangle no :END: Listing [[lst:configure-orderless]] configures [[info:orderless#Company][orderless for company (info)]]. Note: *Python editing is orders of magnitude faster after removal of Orderless*. #+caption[Configure =orderless=]: #+caption: Configure =orderless=. #+name: lst:configure-orderless #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'marginalia) (with-eval-after-load 'orderless (setopt orderless-component-separator " +") (defun just-one-face (fn &rest args) (let ((orderless-match-faces [completions-common-part])) (apply fn args))) (advice-add 'company-capf--candidates :around #'just-one-face))) #+end_src ** [[info:embark#Top][Embark (info)]] :PROPERTIES: :CUSTOM_ID: sec:embark-configuration :END: Listing [[lst:bind-embark-commands][bind =embark= commands]] binds =embark= commands to the global key map: 1. =embark-act= prompts the user for an action and performs it. 2. Except for highlighting of email and web URLs, =embark-dwim= englobes the functionality of src_emacs-lisp{(find-library "goto-addr")} that activates mail and web URLs to turn them into clickable buttons. Since =embark-dwim= acts on a superset of target types, it renders =goto-addr= superfluous. See: 1. [[info:emacs#Goto Address mode][Goto Address mode (info)]]. 2. [[info:embark#The default action on a target][The default (embark-dwim) action on a target (info)]]. 3. =embark-bindings= allows to explore all current command key bindings in the minibuffer. 4. The initialization src_emacs-lisp{(setq prefix-help-command #'embark-prefix-help-command)} enables minibuffer help after a prefix key (for instance {{{kbd(C-x)}}} or {{{kbd(C-c)}}}) as typing {{{kbd(C-x C-h)}}} or {{{kbd(C-c C-h)}}} shows. #+caption[Bind =embark= commands globally]: #+caption: Bind =embark= commands globally. #+name: lst:bind-embark-commands #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'embark 'embark-consult) (when (fboundp 'embark-act) (keymap-global-set "C-," #'embark-act)) (when (fboundp 'embark-dwim) (keymap-global-set "C-:" #'embark-dwim)) (when (fboundp 'embark-bindings) (keymap-global-set "C-h B" #'embark-bindings)) (when (fboundp 'embark-prefix-help-command) (setq prefix-help-command #'embark-prefix-help-command))) #+end_src ** [[info:marginalia#Top][Marginalia (info)]] :PROPERTIES: :CUSTOM_ID: sec:marginalia-configuration :END: Listing [[lst:enable-marginalia-mode]] enables =marginalia-mode=. #+caption[Enable =marginalia-mode=]: #+caption: Enable =marginalia-mode=. #+name: lst:enable-marginalia-mode #+begin_src emacs-lisp -n :results silent (when (and (ensure-package-installation 'marginalia) (fboundp 'marginalia-mode)) (marginalia-mode +1)) #+end_src ** [[info:consult#Top][Consult (info)]] :PROPERTIES: :CUSTOM_ID: sec:consult-setup :END: [[info:consult#Top][Consult (info)]] provides practical commands based on the Emacs minibuffer completion function [[info:elisp#Minibuffer Completion][completing-read]]. Listing [[lst:bind-consult-commands][bind =consult= commands]] binds =consult= commands to different key maps. #+attr_latex: :booktabs yes :float table #+caption[Configuration specific key bindings]: #+caption: Configuration specific key-bindings. #+name: tab:configuration-specific-key-bindings |-----------------------------+----------------------+--------------------| | command | key map | keys | |-----------------------------+----------------------+--------------------| | consult-bookmark | ctl-x-r-keymap | {{{kbd(C-x r b)}}} | | consult-buffer-other-frame | ctl-x-5-keymap | {{{kbd(C-x 5 b)}}} | | consult-buffer-other-window | ctl-x-4-keymap | {{{kbd(C-x 4 b)}}} | | consult-buffer | ctl-x-keymap | {{{kbd(C-x b)}}} | | consult-compile-error | goto-map | {{{kbd(M-g e)}}} | | consult-complex-command | ctl-x-keymap | {{{kbd(C-x M-:)}}} | | consult-find | search-map | {{{kbd(M-s f)}}} | | consult-focus-lines | search-map | {{{kbd(M-s u)}}} | | consult-git-grep | search-map | {{{kbd(M-s g)}}} | | consult-global-mark | goto-map | {{{kbd(M-g k)}}} | | consult-goto-line | goto-map | {{{kbd(M-g M-g)}}} | | consult-goto-line | goto-map | {{{kbd(M-g g)}}} | | consult-history | global-map | {{{kbd(C-c h)}}} | | consult-imenu | goto-map | {{{kbd(M-g i)}}} | | consult-keep-lines | search-map | {{{kbd(M-s k)}}} | | consult-line | search-map | {{{kbd(M-s l)}}} | | consult-mark | goto-map | {{{kbd(M-g m)}}} | | consult-mode-command | global-map | {{{kbd(C-c m)}}} | | consult-multi-occur | search-map | {{{kbd(M-s m)}}} | | consult-outline | goto-map | {{{kbd(M-g o)}}} | | consult-register | ctl-x-r-keymap | {{{kbd(C-x r x)}}} | | consult-yank-pop | global-map | {{{kbd(M-y)}}} | |-----------------------------+----------------------+--------------------| | elfeed | global-map | {{{kbd(C-x w)}}} | | embark-act | global-map | {{{kbd(C-\,)}}} | | embark-bindings | global-map | {{{kbd(C-h B)}}} | | embark-dwim | global-map | {{{kbd(C-:)}}} | | iedit-mode | global-map | {{{kbd(C-;)}}} | | minibuffer-complete-history | minibuffer-local-map | {{{kbd(C-)}}} | | narrow-or-widen-dwim | ctl-x-keymap | {{{kbd(C-x C-n)}}} | | org-agenda | global-map | {{{kbd(C-c a)}}} | | org-capture | global-map | {{{kbd(C-c c)}}} | | org-cite | org-mode-map | {{{kbd(C-c b)}}} | | org-insert-link-global | global-map | {{{kbd(C-c C-l)}}} | | org-narrow-to-table | ctl-x-keymap | {{{kbd(C-x n t)}}} | | org-store-link | global-map | {{{kbd(C-c l)}}} | |-----------------------------+----------------------+--------------------| #+caption[Bind =consult= commands]: #+caption: Bind =consult= commands. #+name: lst:bind-consult-commands #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'consult) ;; C-c bindings (current-global-map) (keymap-global-set "C-c h" #'consult-history) (keymap-global-set "C-c m" #'consult-mode-command) ;; C-x bindings (ctl-x-map) (keymap-set ctl-x-map "M-:" #'consult-complex-command) (keymap-set ctl-x-map "b" #'consult-buffer) (keymap-set ctl-x-4-map "b" #'consult-buffer-other-window) (keymap-set ctl-x-5-map "b" #'consult-buffer-other-frame) (keymap-set ctl-x-r-map "x" #'consult-register) (keymap-set ctl-x-r-map "b" #'consult-bookmark) ;; M-g bindings (goto-map) (keymap-set goto-map "g" #'consult-goto-line) (keymap-set goto-map "M-g" #'consult-goto-line) (keymap-set goto-map "o" #'consult-outline) (keymap-set goto-map "m" #'consult-mark) (keymap-set goto-map "k" #'consult-global-mark) (keymap-set goto-map "i" #'consult-imenu) (keymap-set goto-map "e" #'consult-compile-error) ;; M-s bindings (search-map) (keymap-set search-map "g" #'consult-git-grep) (keymap-set search-map "f" #'consult-find) (keymap-set search-map "k" #'consult-keep-lines) (keymap-set search-map "l" #'consult-line) (keymap-set search-map "m" #'consult-multi-occur) (keymap-set search-map "u" #'consult-focus-lines) ;; Other bindings (current-global-map) (keymap-global-set "M-y" #'consult-yank-pop)) #+end_src ** [[https://company-mode.github.io/][Company: a modular complete anything framework for Emacs]] :PROPERTIES: :CUSTOM_ID: sec:company-setup :END: Listing [[lst:setup-company]] configures =company=. #+caption[Setup =company=]: #+caption: Setup =company=. #+name: lst:setup-company #+begin_src emacs-lisp -n :results silent (when (fboundp 'company-mode) ;; https://github.com/purcell/emacs.d/issues/778 (setopt company-transformers '(company-sort-by-occurrence)) (dolist (hook '(LaTeX-mode-hook org-mode-hook emacs-lisp-mode-hook lisp-interaction-mode-hook lisp-mode-hook python-mode-hook ielm-mode-hook sly-mrepl-mode-hook)) (add-hook hook #'company-mode))) #+end_src * [[info:emacs#Search][Search and replace (info)]] :PROPERTIES: :CUSTOM_ID: sec:search-replace :END: ** [[info:emacs#Regexp Replace][Regexp Replace (info)]] :PROPERTIES: :CUSTOM_ID: sec:regexp-replace :END: Executing {{{kbd(M-x replace-regexp)}}} prompts fo a regular expression =REGEXP= and a substitution =NEWSTRING= to replace =REGEXP= with =NEWSTRING=. The tools in section [[#sec:narrowing][Narrowing]] allow to limit the scope of {{{kbd(M-x replace-regexp)}}}. A list pointing to relevant documentation is: 1. [[info:emacs#Regexp Replace][Regexp Replace (info)]] explains how the substitution handles references to regular expression groups and lisp expressions. 2. [[info:emacs#Regexps][Syntax of Regular Expressions (info)]] describes regular expression features for users. 3. [[info:elisp#Regular Expressions][Emacs Lisp Regular Expressions (info)]] describes regular expression features for programmers. 4. [[https://www.emacswiki.org/][Emacs Wiki]] has an article on [[https://www.emacswiki.org/emacs/ReplaceRegexpWithLispExpressions][replacing regexp with lisp expressions]]. Table [[tab:replace-regexp-tranform]] tabulates how to perfom example tasks by means of {{{kbd(M-x replace-regexp)}}} using =(regular-expression transform)= pairs. #+attr_latex: :booktabs yes :float table #+caption[Example =replace-regexp= (regular-expression substitution) pairs]: #+caption: Example =replace-regexp= (regular-expression substitution) pairs. #+name: tab:replace-regexp-tranform | task | regular expression | substitution | |-----------------------------+---------------------------------+---------------| | move dot two digits forward | =0\.\([[:digit:]][[:digit:]]\)= | =0\1.= | | move dot two digits forward | =0\.\([0-9][0-9]\)= | =0\1.= | | renumber grep/occur matches | =\(.+:\)= | =\,(1+ \#)._= | |-----------------------------+---------------------------------+---------------| ** [[https://github.com/Wilfred/deadgrep#readme][Deadgrep]] :noexport: :PROPERTIES: :CUSTOM_ID: sec:deadgrep :header-args:emacs-lisp: :tangle no :eval never-export :END: [[https://github.com/Wilfred/deadgrep#readme][Deadgrep]] uses [[https://github.com/BurntSushi/ripgrep#readme][ripgrep]] for superfast text searching in the default directory or the current [[https://en.wikipedia.org/wiki/Version_control][VCS]] directory tree. Listing [[lst:bind-deadgrep-commands][setup =deadgrep= commands]] binds =deadgrep= globally to {{{kbd(M-s d)}}} and =deadgrep-edit-mode= locally to {{{kbd(C-c C-w)}}}. #+caption[Bind =deadgrep= commands]: #+caption: Bind =deadgrep= commands. #+name: lst:bind-deadgrep-commands #+begin_src emacs-lisp -n :results silent (when (and (ensure-package-installation 'deadgrep) (fboundp 'deadgrep)) (keymap-set search-map "d" #'deadgrep) (with-eval-after-load 'deadgrep (keymap-set deadgrep-mode-map "C-c C-w" #'deadgrep-edit-mode))) #+end_src ** [[https://www.masteringemacs.org/article/searching-buffers-occur-mode][Searching and Editing in Buffers with Occur Mode]] :PROPERTIES: :CUSTOM_ID: sec:occur :END: #+caption[Define =multi-occur-in-this-mode=]: #+caption: Define =multi-occur-in-this-mode=. #+name: lst:multi-occur-in-this-mode #+begin_src emacs-lisp -n :results silent (defun get-buffers-matching-mode (mode) "Return a list of buffers whose major-mode is equal to MODE." (let ((buffer-mode-matches '())) (dolist (buf (buffer-list)) (with-current-buffer buf (when (eq mode major-mode) (push buf buffer-mode-matches)))) buffer-mode-matches)) (defun multi-occur-in-this-mode () "Show all lines matching REGEXP in buffers with this major mode." (interactive) (multi-occur (get-buffers-matching-mode major-mode) (car (occur-read-primary-args)))) #+end_src ** [[https://www.genivia.com/get-ugrep.html][Ugrep]] :PROPERTIES: :CUSTOM_ID: sec:ugrep :END: [[https://www.genivia.com/get-ugrep.html][Ugrep]] is an ultra fast grep with interactive query UI and fuzzy search. The pages [[https://github.com/Genivia/ugrep#emacs][Using ugrep within Emacs]] and [[https://manueluberti.eu/emacs/2022/08/07/emacs-ugrep/][Ugrep in Emacs]] show how to make Emacs use it with [[info:emacs#Grep Searching][lgrep]] and [[info:emacs#Xref][xref]]. Listing [[lst:set-grep+xref-options][set =grep= and =xref= options]] shows my implementation of the suggestions on those pages. #+caption[Set =grep= and =xref= options to use =ugrep= when available]: #+caption: Set =grep= and =xref= options to use =ugrep= when available. #+name: lst:set-grep+xref-options #+begin_src emacs-lisp -n :results silent (when (executable-find "ugrep") (with-eval-after-load 'grep (setopt grep-template (string-join '("ugrep" "--color=always" "--decompress" "--ignore-binary" "--ignore-case" "--include=\"\"" "--line-number" "--null" "--recursive" "--regexp=") " ") grep-command "ugrep --color=always --null -nH -e " grep-use-null-device nil)) (with-eval-after-load 'xref (setopt xref-search-program 'ugrep xref-search-program-alist `((grep . "xargs -0 grep --null -snHE -e ") (ripgrep . ,(concat "xargs -0 rg --null -nH" " --no-heading --no-messages -g '!*/' -e ")) (ugrep . "xargs -0 ugrep --null -ns -e "))))) #+end_src ** [[https://github.com/mattiase/xr#readme][XR - Emacs regexp parser and analyzer]] :PROPERTIES: :CUSTOM_ID: sec:ensure-xr-installation :END: [[https://github.com/mattiase/xr#readme][XR]] converts Emacs regular expressions to the structured =rx= form, thus being an inverse of =rx=. It can also find mistakes and questionable constructs inside regexp strings. #+caption[Ensure =xr= installation]: #+caption: Ensure =xr= installation. #+name: lst:ensure-xr-installation #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'xr)) #+end_src * [[info:emacs#Version Control][Version Control (info)]] :PROPERTIES: :CUSTOM_ID: sec:version-control :END: ** [[info:ediff#Top][Ediff (info)]] :PROPERTIES: :CUSTOM_ID: sec:ediff :END: Video links to complete the [[info:ediff#Top][ediff (info)]] manual are: 1. [[https://www.youtube.com/watch?v=6ZWp05OW1c0][Emergency Emacs]]. 2. [[https://protesilaos.com/codelog/2020-04-10-emacs-smerge-ediff/][Use =smerge= and =ediff= to resolve file conflicts]]. Listing [[lst:setup-ediff]] configures =ediff= to display all its buffers in a single frame and to make all text visible prior to ediffing Org buffers. #+caption[Setup =ediff=]: #+caption: Setup =ediff=. #+name: lst:setup-ediff #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (setopt ediff-merge-split-window-function #'split-window-horizontally ediff-split-window-function #'split-window-horizontally ediff-window-setup-function #'ediff-setup-windows-plain)) (with-eval-after-load 'org ;; https://github.com/oantolin/emacs-config#readme (defun ediff-with-org-show-all () "Expand all headings prior to ediffing org buffers." (add-hook 'ediff-prepare-buffer-hook #'org-fold-show-all nil t)) (add-hook 'org-mode-hook #'ediff-with-org-show-all)) #+end_src ** [[https://git-scm.com/book/en/v2][Git]] :PROPERTIES: :CUSTOM_ID: sec:git :END: [[https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/git-push-new-branch-remote-github-gitlab-upstream-example][How to push new branches to remote repositories on Github or Gitlab]] explains how to do this from the command line. Listing [[lst:forking-and-branching-remote-repositories]] shows an example work flow of command line =git= and =nano= invocations. I did yet not figure out how to do this by means of [[info:magit#Top][Magit (info)]]: I still have to understand [[info:magit#Pushing][Magit pushing (info)]] and to figure out how to use src_emacs-lisp{(magit-push-current-to-pushremote)} or src_emacs-lisp{(magit-push-current-to-upstream)}. #+caption[Forking and branching on Github or Gitlab]: #+caption: Forking and branching on Github or Gitlab. #+name: lst:forking-and-branching-remote-repositories #+begin_src shell -n :noeval # Clone the repository git clone git@github.com:gav451/engrave-faces engrave-faces-fork cd engrave-face-fork # Handle 1st branch: # - Create and switch to a new branch with either: git switch -c latex-engrave-symbolic-color-names # or: git checkout -b latex-engrave-symbolic-color-names # - Use "--set-upstream origin" as a 1st-time branch push: git push --set-upstream origin latex-engrave-symbolic-color-names # - Create, add, and commit, and push changes in this branch: nano engrave-faces-latex.el git add engrave-faces-latex.el git commit -m 'Commit latex-engrave-dont-symbolic-color-names changes' git push # - Switch to master branch: git checkout master # Handle 2nd branch: # - Create and switch to a new branch with either: git switch -c latex-engrave-dont-wrap-white-space # - or: git checkout -b latex-engrave-dont-wrap-white-space # - Use "--set-upstream origin" as a 1st-time branch push: git push --set-upstream origin latex-engrave-dont-wrap-white-space # - Create, add, commit, and push changes in this branch: nano engrave-faces-latex.el git add engrave-faces-latex.el git commit -m 'Commit latex-engrave-dont-wrap-white-space changes' git push # - Switch to master branch: git checkout master #+end_src *** [[https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/git-push-new-branch-remote-github-gitlab-upstream-example][List all Git branches]] :PROPERTIES: :CUSTOM_ID: sec:list-all-git-branches :END: #+caption[List all =git= branches]: #+caption: List all =git= branches. #+name: lst:list-all-git-branches #+begin_src shell -n :exports both :results verbatim git -C ~/VCS/engrave-faces-fork branch -a #+end_src #+RESULTS: lst:list-all-git-branches : * gav : latex-engrave-dont-wrap-white-space : latex-engrave-symbolic-color-names : master : remotes/origin/HEAD -> origin/master : remotes/origin/gav : remotes/origin/latex-engrave-dont-wrap-white-space : remotes/origin/latex-engrave-symbolic-color-names : remotes/origin/master *** [[info:magit#Top][Magit (info)]] :PROPERTIES: :CUSTOM_ID: sec:magit :END: [[info:magit#Top][Magit (info)]] is an Emacs interface to the version control system [[https://git-scm.com/book/en/v2][Git]] and the snippet [[lst:ensure-magit-installation][below]] ensures its installation. Magit usage tips are: 1. [[https://stackoverflow.com/questions/tagged/magit?tab=Votes][Most upvoted Magit questions on Stack Overflow]]. 2. [[https://stackoverflow.com/a/33644270][Abort an active rebase]] by pressing {{{kbd(r a)}}}. #+caption[Ensure =Magit= installation]: #+caption: Ensure =Magit= installation #+name: lst:ensure-magit-installation #+begin_src emacs-lisp -n :results silent (ensure-package-installation 'magit) #+end_src * Reading :PROPERTIES: :CUSTOM_ID: sec:reading :END: ** Reading [[https://en.wikipedia.org/wiki/DjVu][DjVu]] files :PROPERTIES: :CUSTOM_ID: sec:reading-djvu-files :END: This setup relies on [[info:emacs#Document View][Document View (info)]] to facilitate reading [[https://en.wikipedia.org/wiki/DjVu][DjVu]] files. Reading the code shown by src_emacs-lisp{(find-function 'doc-view-djvu->tiff-converter-ddjvu)} shows that reading [[https://en.wikipedia.org/wiki/DjVu][DjVu]] files requires the command line [[https://en.wikipedia.org/wiki/DjVu][DjVu]] decoder =ddjvu= from the [[http://djvu.sourceforge.net/][DjVuLibre]] utilities. ** Reading EPUB files :PROPERTIES: :CUSTOM_ID: sec:reading-epub-files :END: The package [[https://depp.brause.cc/nov.el/][nov.el]] provides a major mode for reading EPUB files in Emacs. Listing [[lst:configure-nov]] configures [[https://depp.brause.cc/nov.el/][nov.el]]. #+caption[Configure =nov=]: #+caption: Configure =nov=. #+name: lst:configure-nov #+begin_src emacs-lisp -n :results silent (when (and (ensure-package-installation 'nov) (fboundp 'nov-mode)) (add-to-list 'auto-mode-alist `(,(rx ".epub" eos) . nov-mode))) #+end_src ** Reading PDF files :PROPERTIES: :CUSTOM_ID: sec:reading-pdf-files :END: 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. The [[https://github.com/nicolaisingh/saveplace-pdf-view#readme][saveplace-pdf-view]] package saves =pdf-view= and =doc-view= places. In order to use [[https://github.com/vedang/pdf-tools][pdf-tools]], you have to type {{{kbd(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. #+caption[Setup =pdf-tools=]: #+caption: Setup =pdf-tools=. #+name: lst:setup-pdf-tools #+begin_src emacs-lisp -n :results silent (when (and (ensure-package-installation 'pdf-tools 'saveplace-pdf-view) (fboundp 'pdf-loader-install)) ;; `pdf-loader-install' is the lazy equivalent of `pdf-tools-install': ;; see the README file. (pdf-loader-install) (with-eval-after-load 'pdf-outline ;; Unmask the `image-save' key binding in `pdf-view-mode-map' and ;; in `image-mode-map' (by parenthood). (keymap-set pdf-outline-minor-mode-map "o" nil) (keymap-set pdf-outline-minor-mode-map "O" #'pdf-outline)) (with-eval-after-load 'pdf-view (setopt pdf-view-display-size 'fit-page pdf-view-use-scaling (eq system-type 'darwin)) (require 'saveplace-pdf-view nil 'noerror))) #+end_src * Writing :PROPERTIES: :CUSTOM_ID: sec:writing :END: ** Writing LaTeX files :PROPERTIES: :CUSTOM_ID: sec:writing-latex-files :END: Loading =tex.el= immediately instead of lazily ensures proper initialization of [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]]. In particular, 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~. The list below summarizes the main [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] configuration objectives: 1. Listing [[lst:set-tex-options]] ensures installation of [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] and initializes [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] properly for =LuaTeX= or =LuaLaTeX=. 2. Listing [[lst:set-tex-options]] also enables [[https://git.savannah.gnu.org/cgit/auctex.git/patch/?id=f464242eab092e610dda6b654e6fd197ef649d3eAA][indenting between square brackets]] which is a recent feature. 3. Listing [[lst:update-lualatex-opentype-font-name-database]] defines a function to update the =OpenType Font= name database for =LuaLaTeX= when the output of =LuaLaTeX= shows missing fonts. 4. Listing [[lst:set-bibtex-options]] configures the Emacs =bibtex= library to use the LaTeX =BiBTeX= dialect for backwards compatibility. 5. Listing [[lst:set-font-latex-options]] disables font scaling of section titles. 6. Listing [[lst:set-latex-options]] configures =latex= for a full featured =LaTeX-section-command=. #+caption[Ensure =AUCTeX= installation and set =TeX= option]: #+caption: Ensure =AUCTeX= installation and set =TeX= option. #+name: lst:set-tex-options #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'auctex) ;; Use `require' to make `TeX-master' a safe local variable. (when (require 'tex nil 'noerror) (setopt TeX-auto-save t TeX-engine 'luatex TeX-indent-close-delimiters "]" TeX-indent-open-delimiters "[" TeX-install-font-lock #'font-latex-setup TeX-parse-self t ;; Disable `TeX-electric-math' to prevent collisions with `smartparens'. TeX-electric-math nil))) #+end_src #+caption[Update the =LuaLaTeX OpenType Font= name database]: #+caption: Update the =LuaLaTeX= =OpenType Font= name database. #+name: lst:update-lualatex-opentype-font-name-database #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (defun update-lualatex-opentype-font-name-database () "Update the \"OpenType Font\" name database for \"LuaLaTeX\"." (interactive) (cl-destructuring-bind (exit-code output) (shell-command-with-exit-code "luaotfload-tool" "-vv" "--update" "--force") (if (= 0 exit-code) (message "%s" (string-trim output)) (error "%s" (string-trim output)))))) #+end_src #+caption[Set =bibtex= options]: #+caption: Set =bibtex= options. #+name: lst:set-bibtex-options #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'bibtex (setopt bibtex-dialect 'BibTeX)) #+end_src #+caption[Set =font-latex= options]: #+caption: Set =font-latex= options. #+name: lst:set-font-latex-options #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'font-latex (setopt font-latex-fontify-sectioning 1.0)) #+end_src #+caption[Set =latex= options]: #+caption: Set =latex= options. #+name: lst:set-latex-options #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'latex (setopt LaTeX-electric-left-right-brace t LaTeX-section-hook '(LaTeX-section-heading LaTeX-section-title LaTeX-section-toc LaTeX-section-section LaTeX-section-label))) #+end_src *** [[https://gitlab.com/matsievskiysv/math-preview/-/blob/master/README.md][Math-preview]] :PROPERTIES: :CUSTOM_ID: sec:math-preview :END: [[https://gitlab.com/matsievskiysv/math-preview/-/blob/master/README.md][Math-preview]] uses [[https://docs.mathjax.org/en/latest/][Mathjax-3]] to display [[https://www.latex-project.org/][LaTeX]], [[https://www.w3.org/Math/][MathML]],and [[http://asciimath.org/][asciimath]] in Emacs buffers with help of an external [[https://nodejs.org][node.js]] program. #+caption[Setup =math-preview=]: #+caption: Setup =math-preview=. #+name: lst:setup-math-preview #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'math-preview) (with-eval-after-load 'math-preview ;; https://docs.mathjax.org/en/latest/input/tex/extensions/physics.html (let ((physics (split-string " abs absolutevalue acomm acos acosecant acosine acot acotangent acsc admat anticommutator antidiagonalmatrix arccos arccosecant arccosine arccot arccotangent arccsc arcsec arcsecant arcsin arcsine arctan arctangent asec asecant asin asine atan atangent bmqty bqty Bqty bra braket comm commutator cos cosecant cosh cosine cot cotangent coth cp cross crossproduct csc csch curl dd derivative det determinant diagonalmatrix diffd differential div divergence dmat dotproduct dv dyad erf ev eval evaluated exp expectationvalue exponential expval fderivative fdv flatfrac functionalderivative grad gradient gradientnabla hypcosecant hypcosine hypcotangent hypsecant hypsine hyptangent identitymatrix Im imaginary imat innerproduct ip ket ketbra laplacian ln log logarithm matrixdeterminant matrixel matrixelement matrixquantity mdet mel mqty naturallogarithm norm op order outerproduct partialderivative paulimatrix pb pderivative pdv pmat pmqty Pmqty poissonbracket pqty Pr principalvalue Probability pv PV qall qand qas qassume qc qcc qcomma qelse qeven qfor qgiven qif qin qinteger qlet qodd qor qotherwise qq qqtext qsince qthen qty quantity qunless qusing rank Re real Res Residue sbmqty sec secant sech sin sine sinh smallmatrixquantity smdet smqty spmqty sPmqty svmqty tan tangent tanh tr Tr trace Trace va var variation vb vdot vectorarrow vectorbold vectorunit vmqty vnabla vqty vu xmat xmatrix zeromatrix zmat"))) (cl-pushnew `("physics" . ,physics) math-preview-tex-packages-autoload-packages)) (setq math-preview-raise 0.5 math-preview-scale 1 math-preview-tex-default-packages '("autoload" "ams" "physics")) (let ((command (executable-find "~/node_modules/.bin/math-preview"))) (if command (setq math-preview-command command) ;; https://stackoverflow.com/a/17509764 answers: ;; How to install an npm package from github directly? (unless (file-directory-p "~/node_modules") (make-directory "~/node_modules")) (cl-destructuring-bind (exit-code output) (shell-command-with-exit-code "npm" "install" "git+https://gitlab.com/matsievskiysv/math-preview.git") (if (= 0 exit-code) (message "%s" (string-trim output)) (error "%s" (string-trim output)))))))) #+end_src *** TODO Improve the AUCTeX configuration slowly :PROPERTIES: :CUSTOM_ID: sec:setup-auctex-slowly :END: [[https://github.com/thisirs/dotemacs/blob/master/lisp/init-auctex.el][AUCTeX setup of an experienced user]] ** Writing [[https://www.markdownguide.org/][Markdown]] files :PROPERTIES: :CUSTOM_ID: sec:writing-markdown-files :END: #+caption[Configure =markdown-mode=]: #+caption: Configure =markdown-mode=. #+name: lst:configure-markdown-mode #+begin_src emacs-lisp -n :results silent (when (ensure-package-installation 'markdown-mode)) #+end_src ** Writing [[info:org#Top][Org (info)]] files :PROPERTIES: :CUSTOM_ID: sec:writing-org-files :END: The [[https://org-babel.readthedocs.io/en/latest/][Org Babel reference card]] complements section [[info:org#Working with Source Code][Working with Source Code (info)]] of the [[info:org#Top][Org (info)]] manual. *** [[info:org#Activation][Org activation (info)]] :PROPERTIES: :CUSTOM_ID: sec:activate-org :END: #+caption[Bind =Org= commands globally]: #+caption: Bind =Org= commands globally. #+name: lst:bind-org-commands #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (global-set-key (kbd "C-c a") #'org-agenda) (global-set-key (kbd "C-c c") #'org-capture) (with-eval-after-load 'ol (global-set-key (kbd "C-c l") #'org-store-link) (global-set-key (kbd "C-c C-l") #'org-insert-link-global))) #+end_src *** Setup Org :PROPERTIES: :CUSTOM_ID: sec:setup-org :END: I have split the initial [[https://orgmode.org/][Org-mode]] setup over several listings. Here, follows a list detailing and motivating each listing: 1. Listing [[lst:set-org-options]] handles basic [[https://orgmode.org/][Org-mode]] options. 2. Listing [[lst:setup-org-babel]] handles basic [[https://orgmode.org/][Org-mode]] options specific to [[info:org#Working with Source Code][work with source code (info)]]. 3. Listing [[lst:set-org-link-options]] handles [[https://orgmode.org/][Org-mode]] options specific to [[info:org#Hyperlinks][hyperlinks (info)]]. 4. Listing [[lst:setup-org-mode-map]] extends the =org-mode-map= with useful key-bindings. 5. Listing [[lst:setup-org-src]] facilitates [[info:org#Editing Source Code][editing Python source code blocks]], and it provides a function to remove the indentation of all =org-src-mode= blocks without =-i= switch. 6. Listing [[lst:setup-ob-python]] allows to pretty-print Python session source block values with [[https://github.com/psf/black#readme][black]] instead of [[https://docs.python.org/3/library/pprint.html][pprint]]. 7. Listing [[lst:set-org-export-options]] selects the =non-intrusive= expert user interface for export dispatching. 8. Listing [[lst:setup-org-for-lualatex-export]] and [[lst:set-ox-latex-options-for-lualatex-export]] configure [[https://orgmode.org/][Org-mode]] to generate output for the LuaLaTeX compiler. 9. Listing [[lst:setup-org-latex-classes]] defines [[info:org#LaTeXspecificexportsettings][org-latex-classes (info)]] for backward compatibility. See table [[tab:org-latex-class-tag-placeholder]] and type {{{kbd(C-hv org-latex-classes)}}} for an explanation of the code in listing [[lst:setup-org-latex-classes]]. #+caption[Set basic =Org= options]: #+caption: Set basic =Org= options. #+name: lst:set-org-options #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'org (setopt org-babel-load-languages `((C . t) (calc . t) (dot . ,(fboundp 'grapviz-dot-mode)) (emacs-lisp . t) (eshell . t) (fortran . t) (gnuplot . ,(fboundp 'gnuplot-mode)) (js . t) (latex . t) (lilypond . ,(fboundp 'lilypond-mode)) (lisp . t) (lua . ,(fboundp 'lua-mode)) (maxima . ,(fboundp 'maxima-mode)) (org . t) (perl . t) ;; Beware: Python activation must not call still unbound functions. (python . t) ;; MIT/GNU Scheme breaks Babel contrary to Guile, Chez and Chicken Scheme. (scheme . ,(fboundp 'geiser-mode)) (shell . t)) org-export-backends '(ascii beamer html icalendar latex my-html odt texinfo) org-file-apps '((auto-mode . emacs) (directory . emacs) ("\\.mm\\'" . default) ("\\.x?html?\\'" . default) ("\\.pdf\\'" . emacs)) org-modules '(ol-bibtex ol-doi ol-eww ol-info org-id org-protocol org-tempo))) #+end_src #+caption[Setup =org-babel=]: #+caption: Setup =org-babel=. #+name: lst:setup-org-babel #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ob-core (setopt org-confirm-babel-evaluate nil)) (with-eval-after-load 'ob-latex (setopt org-babel-latex-preamble (lambda (_) "\\documentclass[preview]{standalone}\n") org-babel-latex-begin-env (lambda (_) "\\begin{document}\n") org-babel-latex-end-env (lambda (_) "\\end{document}\n"))) (with-eval-after-load 'ob-lisp ;; Default to `sly-eval' whenever feasible: (when (package-installed-p 'sly) (setopt org-babel-lisp-eval-fn #'sly-eval))) (with-eval-after-load 'emacs (defun set-org-babel-language-activity (lang active) "Set the `org-babel' activity of language LANG to ACTIVE. Let `org-babel-do-load-languages' load ob-LANG when ACTIVE is t or unbind the `org-babel' interface functions when ACTIVE is nil." (if (locate-library (format "ob-%s" lang)) (or (org-babel-do-load-languages 'org-babel-load-languages (cons (cons lang active) (assq-delete-all lang (default-toplevel-value 'org-babel-load-languages)))) org-babel-load-languages) (warn "Can't locate `ob-%s', hence don't set `%s' activity" lang lang))) (defun activate-org-babel-language (lang) "Activate LANG for `org-babel'." (interactive "SActivate language: ") (if (set-org-babel-language-activity lang t) (message "Did activate `%s' for `org-babel'" lang) (message "Can't activate `%s' for `org-babel'" lang))) (defun deactivate-org-babel-language (lang) "Activate LANG for `org-babel'." (interactive "SDeactivate language: ") (if (set-org-babel-language-activity lang nil) (message "Did deactivate `%s' for `org-babel'" lang) (message "Can't deactivate `%s' for `org-babel'" lang)))) #+end_src #+caption[Set =org-link= options]: #+caption: Set =org-link= options. #+name: lst:set-org-link-options #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ol (setopt org-link-file-path-type 'relative)) #+end_src #+caption[Setup =org-mode-map=]: #+caption: Setup =org-mode-map=. #+name: lst:setup-org-mode-map #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; From: "Nicolas Richard" ;; Date: Fri, 08 Mar 2013 16:23:02 +0100 [thread overview] ;; Message-ID: <87vc913oh5.fsf@yahoo.fr> (raw) (defun org-electric-dollar () "When called once, insert \\(\\) and leave point in between. When called twice, replace the previously inserted \\(\\) by one $." (interactive) (if (and (looking-at "\\\\)") (looking-back "\\\\(" (- (point) 2))) (progn (delete-char 2) (delete-char -2) (insert "$")) (insert "\\(\\)") (backward-char 2))) (defun org-active-current-time-stamp () "Insert an active date-time stamp of the current time." (interactive) (if (fboundp 'org-insert-time-stamp) (org-insert-time-stamp (current-time) 'with-hm) (let ((stamp (format-time-string "<%Y-%m-%d %a %H:%M>" (current-time)))) (insert stamp) stamp))) (defun org-inactive-current-time-stamp () "Insert an inactive date-time stamp of the current time." (interactive) (if (fboundp 'org-insert-time-stamp) (org-insert-time-stamp (current-time) 'with-hm 'inactive) (let ((stamp (format-time-string "[%Y-%m-%d %a %H:%M]" (current-time)))) (insert stamp) stamp))) (with-eval-after-load 'org (setopt org-return-follows-link t) (keymap-set org-mode-map "C-c " #'org-inactive-current-time-stamp) (keymap-set org-mode-map "C-c C-" #'org-active-current-time-stamp) (keymap-set org-mode-map "$" #'org-electric-dollar) (keymap-set org-mode-map "M-q" #'org-fill-paragraph))) #+end_src #+caption[Setup =org-src=]: #+caption: Setup =org-src=. #+name: lst:setup-org-src #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (with-eval-after-load 'org-src (setopt org-src-preserve-indentation t)) (declare-function org-babel-map-src-blocks "ext:ob-core" (file &rest body)) (declare-function org-do-remove-indentation "ext:org-macs" (n skip-fl)) (defun zero-all-org-src-blocks-indentation () "Remove the indentation of all `org-src-mode' blocks without `-i' switch." (interactive) (when (derived-mode-p 'org-mode) (save-excursion (org-babel-map-src-blocks nil (unless (or (string-match "-i " switches) (string-match (rx (or bos bol) graph) body)) (when-let ((new (with-temp-buffer (insert body) (org-do-remove-indentation) (buffer-string)))) (goto-char beg-body) (delete-region beg-body end-body) (insert new)))))))) #+end_src #+caption[Setup =ob-python=]: #+caption: Setup =ob-python=. #+name: lst:setup-ob-python #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ob-python (defun org-babel-python-format-session-value-override (src-file result-file result-params) "Return Python code to evaluate SRC-FILE and write result to RESULT-FILE. Use `black' instead of `pprint' when \"pp\" is a member of RESULT-PARAMS." (format "\ import ast with open('%s') as __org_babel_python_tmpfile: __org_babel_python_ast = ast.parse(__org_babel_python_tmpfile.read()) __org_babel_python_final = __org_babel_python_ast.body[-1] if isinstance(__org_babel_python_final, ast.Expr): __org_babel_python_ast.body = __org_babel_python_ast.body[:-1] exec(compile(__org_babel_python_ast, '', 'exec')) __org_babel_python_final = eval( compile(ast.Expression(__org_babel_python_final.value), '', 'eval') ) with open('%s', 'w') as __org_babel_python_tmpfile: if %s: import black __org_babel_python_tmpfile.write( black.format_str(repr(__org_babel_python_final), mode=black.Mode()) ) else: __org_babel_python_tmpfile.write(str(__org_babel_python_final)) else: exec(compile(__org_babel_python_ast, '', 'exec')) __org_babel_python_final = None" (org-babel-process-file-name src-file 'noquote) (org-babel-process-file-name result-file 'noquote) (if (member "pp" result-params) "True" "False"))) (advice-add 'org-babel-python-format-session-value :override #'org-babel-python-format-session-value-override)) #+end_src #+caption[Set =org-export= options]: #+caption: Set =org-export= options. #+name: lst:set-org-export-options #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ox (setopt org-export-dispatch-use-expert-ui t)) #+end_src #+caption[Setup =org= for export to LuaLaTeX]: #+caption: Setup =org= for export to LuaLaTeX. #+name: lst:setup-org-for-lualatex-export #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'org ;; https://list.orgmode.org/87o84fd4oi.fsf@posteo.net/ (add-to-list 'org-preview-latex-process-alist '(luamagick :programs ("lualatex" "convert") :description "pdf > png" :message "you need to install lualatex and imagemagick." :image-input-type "pdf" :image-output-type "png" :image-size-adjust (1.0 . 1.0) :post-clean nil :latex-header nil :latex-compiler ("lualatex -interaction nonstopmode -output-directory %o %f") :image-converter ("convert -density %D -trim -antialias %f -quality 100 %O"))) (setopt org-preview-latex-default-process 'luamagick org-latex-default-packages-alist '(("" "fontspec" t ("lualatex")) ("AUTO" "inputenc" t ("pdflatex")) ("T1" "fontenc" t ("pdflatex")) ("" "graphicx" t) ("" "longtable" nil) ("" "wrapfig" nil) ("" "rotating" nil) ("normalem" "ulem" t) ("" "amsmath" t) ("" "amssymb" t) ("" "capt-of" nil) ("" "hyperref" nil)))) #+end_src #+caption[Set =ox-latex= options for export to LuaLaTeX]: #+caption: Set =ox-latex= options for export to LuaLaTeX. #+name: lst:set-ox-latex-options-for-lualatex-export #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ox-latex (setopt org-latex-compiler "lualatex" org-latex-hyperref-template "\\hypersetup{ pdfauthor={%a}, pdftitle={%t}, pdfkeywords={%k}, pdfsubject={%d}, pdfcreator={%c}, pdflang={%L}, citecolor=blue, colorlinks=true, filecolor=blue, hyperfootnotes=false, linkcolor=blue, unicode=true, urlcolor=blue, }\n" org-latex-src-block-backend 'minted org-latex-minted-langs '((cc "c++") (conf "text") (cperl "perl") (diff "diff") (shell-script "bash") (json "json") (org "text") (toml "toml")) org-latex-minted-options '(("bgcolor" "LightGoldenrodYellow")) org-latex-logfiles-extensions (cl-union '("lof" "lot") org-latex-logfiles-extensions :test #'equal) ;; https://tecosaur.github.io/emacs-config/#compiling org-latex-pdf-process (list (concat "latexmk -f -pdf -%latex" " -interaction=nonstopmode" " -shell-escape -outdir=%o %f")) org-latex-prefer-user-labels t org-latex-subtitle-separate t)) #+end_src #+caption[Setup =org-latex-classes= for backwards compatibility]: #+caption: Setup =org-latex-classes= for backwards compatibility. #+name: lst:setup-org-latex-classes #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ox-latex (mapc (function (lambda (element) (add-to-list 'org-latex-classes element))) (nreverse (quote (("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 #+attr_latex: :booktabs yes :float table #+caption[The relation tag-placeholder in listing [[lst:setup-org-latex-classes]]]: #+caption: The relation tag-placeholder in listing [[lst:setup-org-latex-classes]]. #+name: tab:org-latex-class-tag-placeholder | tag | placeholder | |-----+-----------------------| | +1 | [DEFAULT-PACKAGES] | | +2 | [PACKAGES] | | +3 | [EXTRA] | | -1 | [NO-DEFAULT-PACKAGES] | | -2 | [NO-PACKAGES] | | -3 | [NO-EXTRA] | *** Org introspection :PROPERTIES: :CUSTOM_ID: sec:org-introspection :header-args:emacs-lisp: :exports code :tangle no :END: #+caption[Find valid entries for =org-babel-load-languages=]: #+caption: Find valid entries for =org-babel-load-languages=. #+name: lst:valid-org-babel-load-languages-entries #+header: :wrap "src emacs-lisp :results silent :tangle no" #+begin_src emacs-lisp :exports both :results value pp (defun all-org-babel-execute-fns () "Find `ob-LANGUAGE' files in Org defining `org-babel-execute:LANGUAGE'. Return a list of items where the filename is the `car' of each item and the `cdr' of each item lists the `org-babel-execute:LANGUAGE' functions." (let* ((dir (file-name-parent-directory (locate-library "org"))) (names (directory-files dir t (rx "ob-" (+ print) ".el" eos)))) (cl-loop for name in names for found = (has-org-babel-execute-fn name) when found collect found))) (defun has-org-babel-execute-fn (name) (let* ((buffer (find-file-noselect name)) (base (file-name-base (buffer-file-name buffer))) (regexp (rx "defun" (+ blank) "org-babel-execute:" (group (+ graphic)))) (matches)) (save-match-data (save-excursion (with-current-buffer buffer (save-restriction (widen) (goto-char 1) (while (re-search-forward regexp nil t 1) (push (match-string-no-properties 1) matches)))))) (when matches `(,base ,@matches)))) (all-org-babel-execute-fns) #+end_src #+caption[Org Babel libraries with =org-babel-execute:language= functions]: #+caption: Org Babel libraries with =org-babel-execute:language= functions. #+RESULTS: lst:valid-org-babel-load-languages-entries #+begin_src emacs-lisp :results silent :tangle no (("ob-C" "C" "D" "C++" "cpp") ("ob-R" "R") ("ob-awk" "awk") ("ob-calc" "calc") ("ob-clojure" "clojurescript" "clojure") ("ob-css" "css") ("ob-ditaa" "ditaa") ("ob-dot" "dot") ("ob-emacs-lisp" "emacs-lisp") ("ob-eshell" "eshell") ("ob-forth" "forth") ("ob-fortran" "fortran") ("ob-gnuplot" "gnuplot") ("ob-groovy" "groovy") ("ob-haskell" "haskell") ("ob-java" "java") ("ob-js" "js") ("ob-julia" "julia") ("ob-latex" "latex") ("ob-lilypond" "lilypond") ("ob-lisp" "lisp") ("ob-lua" "lua") ("ob-makefile" "makefile") ("ob-maxima" "maxima") ("ob-ocaml" "ocaml") ("ob-octave" "octave" "matlab") ("ob-org" "org") ("ob-perl" "perl") ("ob-plantuml" "plantuml") ("ob-processing" "processing") ("ob-python" "python") ("ob-ruby" "ruby") ("ob-sass" "sass") ("ob-scheme" "scheme") ("ob-screen" "screen") ("ob-sed" "sed") ("ob-shell" "shell") ("ob-sql" "sql") ("ob-sqlite" "sqlite")) #+end_src #+caption[Find active Org Babel languages]: #+caption: Find active Org Babel languages. #+name: lst:org-babel-active-languages #+header: :wrap "src emacs-lisp :results silent :tangle no" #+begin_src emacs-lisp :exports both :results value pp (defun org-babel-active-languages () (let ((result '("conf" "text" "toml"))) (mapatoms (lambda (x) (when (and (string-prefix-p "org-babel-execute:" (symbol-name x)) ;; Get rid of all sub-modes in `ob-shell': (not (eq 'org-babel-shell-initialize (get x 'definition-name))) ;; Get rid of aliases: (not (eq 'symbol (type-of (symbol-function x))))) (when (symbol-file x) (push (string-remove-prefix "org-babel-execute:" (symbol-name x)) result))))) result)) (mapcar #'list (cl-sort (org-babel-active-languages) #'string<)) #+end_src #+caption[Active Org Babel languages]: #+caption: Active Org Babel languages. #+RESULTS: lst:org-babel-active-languages #+begin_src emacs-lisp :results silent :tangle no (("C") ("C++") ("D") ("calc") ("conf") ("cpp") ("emacs-lisp") ("eshell") ("fortran") ("js") ("latex") ("latex-extra-header") ("latex-header") ("lisp") ("maxima") ("org") ("perl") ("scheme") ("shell") ("text") ("toml")) #+end_src *** [[https://github.com/bdarcus/citar][Citar: citing bibliography]] with [[https://orgmode.org/][Org Mode]] :PROPERTIES: :CUSTOM_ID: sec:citing-bibliography :END: [[https://github.com/bdarcus/citar][Citar]] is a completing-read front-end to browse and act on BibTeX, BibLaTeX, as well as CSL JSON bibliographic data with LaTeX, markdown, and org-cite editing support. In combination with vertico, embark, marginalia, and consult, [[https://github.com/bdarcus/citar][Citar]] provides quick filtering and selecting of bibliographic entries from the minibuffer as well as the option to run different commands on those selections. The article [[https://kristofferbalintona.me/posts/202206141852/][Citations in org-mode: Org-cite and Citar]] tries to walk you from understanding the general context (bibliography producer, text processor, text to product convertor) to an Emacs setup. Listing [[lst:set-org-cite+citar-options]] shows my =org-cite=, =citar=, and =org= setup. #+caption[Set =org-cite= and =citar= options]: #+caption: Set =org-cite= and =citar= options. #+name: lst:set-org-cite+citar-options #+begin_src emacs-lisp -n (with-eval-after-load 'oc ;; Org-9.5 needs the requirements, but Org-9.6 does not. (when (version< (org-version) "9.6") (require 'oc-biblatex) (require 'oc-csl))) (with-eval-after-load 'org (keymap-set org-mode-map "C-c b" #'org-cite-insert)) (when (ensure-package-installation 'citar 'citar-embark) (with-eval-after-load 'embark (when (fboundp 'citar-embark-mode) (citar-embark-mode +1))) (when (require 'citar nil 'noerror) (setopt citar-bibliography '("~/VCS/research/refs.bib") citar-library-file-extensions '("djvu" "pdf") citar-library-paths '("~/VCS/research/papers/"))) (with-eval-after-load 'oc (setopt org-cite-export-processors '((latex biblatex) (t csl)) org-cite-global-bibliography '("~/VCS/research/refs.bib")) (when (require 'citar nil 'noerror) (setopt org-cite-activate-processor 'citar org-cite-follow-processor 'citar org-cite-insert-processor 'citar)))) #+end_src Ref. [cite:@Schulte.MCSE.2011.41] shows that [[https://orgmode.org/][Org-mode]] is a simple, plain-text markup language for hierarchical documents allowing intermingling of data, code, and prose. It serves as an example link to to show how to use [[https://github.com/bdarcus/citar][Citar]] within this setup, provided that =citar-bibliography= and =citar-library-paths= point to valid directories and files. In an [[https://orgmode.org/][Org-mode]] buffer this setup allows to: 1. Insert one or more [[https://orgmode.org/][Org-mode]] cite links: 1. Type {{{kbd(C-c b)}}} or {{{kbd(M-x org-cite-insert)}}}. 2. Navigate to a bibliographic entry to insert: 1. Select it with {{{kbd(TAB)}}} to select more entries. 2. Select it with {{{kbd(RET)}}} to select the last entry. 2. View an electronic copy of an [[https://orgmode.org/][Org-mode]] cite link: 1. Move point to the [[https://orgmode.org/][Org-mode]] cite link. 2. Type {{{kbd(C-:)}}} or {{{kbd(M-x embark-dwim)}}}. 3. Navigate to the corresponding library file. 4. Select it. 3. Open the DOI of an [[https://orgmode.org/][Org-mode]] cite link: 1. Move point to the [[https://orgmode.org/][Org-mode]] cite link. 2. Type {{{kbd(C-:)}}} or {{{kbd(M-x embark-dwim)}}}. 3. Navigate to the corresponding link. 4. Select it. *** TODO Compare bibtex and biblatex 1. [[https://www.economics.utoronto.ca/osborne/latex/BIBTEX.HTM][Using bibtex: a short guide]] 2. [[https://www.tug.org/texlive//devsrc/Master/texmf-dist/doc/latex/biblatex/biblatex.pdf][The biblatex package]] 3. [[https://github.com/yuchenlin/rebiber][Rebiber: A tool for normalizing bibtex with official info]] 4. [[https://github.com/josephwright/biblatex-phys][A biblatex implementation of the AIP and APS bibliography style]] #+caption[Delete =Biber= cache when =Biber= fails to make a bibliography]: #+caption: Delete =Biber= cache when =Biber= fails to make a bibliography. #+name: lst:delete-biber-cache #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; https://tex.stackexchange.com/a/579356 answers ;; "How to solve Biber exiting with error code 2 but no error messages?" (defun biber-delete-cache () "Delete the `Biber' cache to get rid of `Biber' exit code 2." (interactive) (cl-destructuring-bind (exit-code output) (shell-command-with-exit-code "rm" "-rf" "$(biber --cache)") (if (= 0 exit-code) (message "%s" (string-trim output)) (error "%s" (string-trim output)))))) #+end_src *** [[https://github.com/tecosaur/engrave-faces#readme][Engrave Faces]] :PROPERTIES: :CUSTOM_ID: sec:engrave-faces :END: This package has a front-end that processes fontified buffers and pipes its output into one of three back-ends for export to LaTeX, HTML and text with ANSI escape codes. It is an alternative to =minted= in case of LaTeX export or to =htmlized= in case of HTML export. The listings below work around one issue with spacing in the =pdf= output resulting from use of the =engraved= source block back-end during =LaTeX= export. They increase the quality of the export results of this [[file:README.org][README.org]] and the user-friendliness of this facility to a level acceptable for my workflow: 1. The default Org export configuration for engraving listings produces =pdf= output with a non-equidistant interline spacing in floating listings (those with captions) and equidistant interline spacing in non-floating =tcolorbox= environments (those without captions). I have traced the non-equidistant interline spacing back to the =breakable= option of =Code= environments inside =listing= environments. However, =tcolorbox= requires =breakable= for listings that do not fit on a page. The filter =org-latex-engraved-source-block-filter= in listing [[lst:org-latex-engraved-source-block-filter]] and the =org-latex-engraved-preamble= customization in listing [[lst:smart-latex-engrave-org-source-blocks]] allow to engrave =org-mode= source blocks to "floating unbreakable with caption" or "non-floating breakable without caption" LaTeX environments. This is a partial work-around, but part of my workflow. 2. Listing [[lst:ox-engraved-emacs-lisp-setup]] shows how to use this configuration. #+caption[Engrave to floating unbreakable or non-floating breakable environments]: #+caption: Define an =org-export= filter function to engrave =org-src-mode= #+caption: blocks to floating unbreakable LaTeX environments or non-floating #+caption: breakable LaTeX environments. #+name: lst:org-latex-engraved-source-block-filter #+begin_src emacs-lisp -n :results silent (ensure-package-installation 'engrave-faces) (defun org-latex-engraved-source-block-filter (data _backend _info) "Replace \"Code\" with \"Breakable\" in non-floating environments. Set `org-latex-engraved-preamble' to define a Breakable (non-floating) environment and an unbreakable Code (floating) environment." (unless (string-match "^\\\\DeclareTColorBox\\[\\]{Breakable}" org-latex-engraved-preamble) (user-error "`org-latex-engraved-preamble' defines no `Breakable' environment")) (when (eq org-latex-src-block-backend 'engraved) (let ((enter "^\\\\begin{Code}\n\\\\begin{Verbatim}") (leave "^\\\\end{Verbatim}\n\\\\end{Code}")) ;; Transform only blocks matching the enter regexp at position 0. ;; Do not transform blocks that are listing environments. (when (and (string-match enter data) (eql 0 (match-beginning 0))) (setq data (replace-match "\\begin{Breakable}\n\\begin{Verbatim}" t t data)) (when (string-match leave data (match-end 0)) (setq data (replace-match "\\end{Verbatim}\n\\end{Breakable}" t t data)) (when (string-match enter data) (user-error "The `enter' regexp `%s' should not match" enter)) (when (string-match leave data) (user-error "The `leave' regexp `%s' should not match" leave)) data))))) #+end_src #+caption[Smart LaTeX engraving of =org-src-mode= blocks]: #+caption: Smart LaTeX engraving of =org-src-mode= blocks. #+name: lst:smart-latex-engrave-org-source-blocks #+begin_src emacs-lisp -n :results silent (defun smart-latex-engrave-org-source-blocks () "Enable smart LaTeX engraving of `org-src-mode' blocks. Sets backend and preamble locally to support floating unbreakable LaTeX environments and non-floating breakable LaTeX environments by means of `org-latex-engraved-source-block-filter'." (interactive) (when (require 'ox-latex nil 'noerror) (setq-local org-latex-src-block-backend 'engraved org-latex-engraved-preamble "\\usepackage{fvextra} [FVEXTRA-SETUP] % Make code and line numbers normalsize. Make line numbers grey. \\renewcommand\\theFancyVerbLine{ \\normalsize\\color{black!40!white}\\arabic{FancyVerbLine}} % In case engrave-faces-latex-gen-preamble has not been run. \\usepackage{xcolor} \\providecolor{EfD}{HTML}{f7f7f7} \\providecolor{EFD}{HTML}{28292e} % To use \\DeclareTColorBox from the tcolorbox package: \\usepackage[breakable,xparse]{tcolorbox} % Define a Breakable environment to fontify code outside floats. \\DeclareTColorBox[]{Breakable}{o}{ colback=EfD, colframe=EFD, colupper=EFD, fontupper=\\normalsize\\setlength{\\fboxsep}{0pt}, IfNoValueTF={#1}{ boxsep=1pt, arc=1pt, outer arc=1pt, boxrule=0.5pt, }{ boxsep=1pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1pt }, left=2pt, right=2pt, top=1pt, bottom=1pt, breakable } % Define an unbreakable Code environment to fontify code inside floats. \\DeclareTColorBox[]{Code}{o}{ colback=EfD, colframe=EFD, colupper=EFD, fontupper=\\normalsize\\setlength{\\fboxsep}{0pt}, IfNoValueTF={#1}{ boxsep=1pt, arc=1pt, outer arc=1pt, boxrule=0.5pt }{ boxsep=1pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1pt }, left=2pt, right=2pt, top=1pt, bottom=1pt } [LISTINGS-SETUP]"))) #+end_src #+caption[Emacs setup to use =engrave-faces-latex= in =org-mode= smartly]: #+caption: Emacs setup to use =engrave-faces-latex= in =org-mode= smartly. #+name: lst:ox-engraved-emacs-lisp-setup #+begin_src emacs-lisp -n :exports code :results none :tangle no (with-eval-after-load 'ox (make-variable-buffer-local 'org-export-filter-src-block-functions) (add-to-list 'org-export-filter-src-block-functions 'org-latex-engraved-source-block-filter) (when (fboundp 'smart-latex-engrave-org-source-blocks) (smart-latex-engrave-org-source-blocks))) #+end_src *** [[info:org#Adding Hyperlink Types][Making Org hyperlink types (info)]] :PROPERTIES: :CUSTOM_ID: sec:make-org-hyperlink-types :END: The listings below implement or reimplement three groups of =org-link= types: 1. Listing [[lst:org-ref-like-org-link-types]] defines =org-link= types for backwards compatibility with [[https://github.com/jkitchin/org-ref][org-ref]]. 2. Listing [[lst:define-org-pdfview-link-type]] uses ideas from the definition of =docview-org-link= and =doi-org-link= types to define an =pdfview-org-link= type for use with [[https://github.com/vedang/pdf-tools][pdf-tools]]. 3. Listing [[lst:patch-org-info-link-type]] patches the function =org-info-export= and listing [[lst:emacs-lisp-setup-buffer-local-ol-info]] sets the constants ~org-info-emacs-documents~ and ~org-info-other-documents~ as buffer local variables in order to export the =info-org-link= types in this document to =html= and LaTeX correctly. #+caption[Define =org-link= types for backwards compatibility with =org-ref=]: #+caption: Define =org-link= types for backwards compatibility with =org-ref=. #+name: lst:org-ref-like-org-link-types #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ol (org-link-set-parameters "ac*" :export #'org-ref-ac*-export) (org-link-set-parameters "cite" :export #'org-ref-cite-export) (org-link-set-parameters "eqref" :export #'org-ref-eqref-export) (org-link-set-parameters "hyperlink" :export #'org-ref-hyperlink-export) (org-link-set-parameters "label" :export #'org-ref-label-export) (org-link-set-parameters "ref" :export #'org-ref-ref-export) (defun org-ref-ac*-export (path _desc backend _info) (pcase backend (`latex (format "\\gls*{%s}" path)) (_ path))) (defun org-ref-cite-export (path _desc backend _info) (pcase backend (`latex (format "\\cite{%s}" path)) (_ path))) (defun org-ref-eqref-export (path _desc backend _info) (pcase backend (`latex (format "\\eqref{%s}" path)) (_ path))) (defun org-ref-hyperlink-export (path desc backend _info) (pcase backend (`latex (format "\\hyperlink{%s}{%s}" path desc)) (_ path))) (defun org-ref-label-export (path _desc backend _info) (pcase backend (`latex (format "\\label{%s}" path)) (_ path))) (defun org-ref-ref-export (path _desc backend _info) (pcase backend (`latex (format "\\ref{%s}" path)) (_ path)))) #+end_src #+caption[Define an =org-link= type for =pdf-tools=]: #+caption: Define an =org-link= type for =pdf-tools=. #+name: lst:define-org-pdfview-link-type #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ol (autoload 'pdf-view-goto-page "pdf-view" nil t) (org-link-set-parameters "pdfview" :follow #'org-pdfview-open :export #'org-pdfview-export :store #'org-pdfview-store-link) (defun org-pdfview-export (link description backend _) "Export a \"pdfview\" type link. The paths of the links export as file relative paths in order to facilate moving single directories or whole directory trees." (let ((path (if (string-match "\\(.+\\)::.+" link) (match-string 1 link) link)) (desc (or description link))) (when (stringp path) (setq path (file-relative-name path)) (pcase backend (`html (format "%s" path desc)) (`latex (format "\\href{%s}{%s}" path desc)) (`ascii (format "%s (%s)" desc path)) (_ path))))) (defun org-pdfview-open (link _) "Open a \"pdfview\" type link." (string-match "\\(.*?\\)\\(?:::\\([0-9]+\\)\\)?$" link) (let ((path (match-string 1 link)) (page (and (match-beginning 2) (string-to-number (match-string 2 link))))) ;; Let Org mode open the file (in-emacs = 1) to ensure ;; org-link-frame-setup is respected. (org-open-file path 1) (when page (pdf-view-goto-page page)))) (defun org-pdfview-store-link () "Store a \"pdfview\" type link." (when (eq major-mode 'pdf-view-mode) (let* ((path buffer-file-name) (page (pdf-view-current-page)) (link (concat "pdfview:" path "::" (number-to-string page)))) (org-link-store-props :type "pdfview" :link link :description path))))) #+end_src #+caption[Patch =ol-info=]: #+caption: Patch =ol-info=. #+name: lst:patch-org-info-link-type #+begin_src emacs-lisp -n :exports code :results silent (with-eval-after-load 'ol-info (defun org-info-export (path desc format) "Export an info link. See `org-link-parameters' for details about PATH, DESC and FORMAT." (let* ((parts (split-string path "#\\|::")) (manual (car parts)) (node (or (nth 1 parts) "Top"))) (pcase format (`html (format "%s" (org-info-map-html-url manual) (org-info--expand-node-name node) (or desc path))) (`latex (format "\\href{%s\\#%s}{%s}" (org-info-map-html-url manual) (org-info--expand-node-name node) (or desc path))) (`texinfo (let ((title (or desc ""))) (format "@ref{%s,%s,,%s,}" node title manual))) (_ nil))))) #+end_src #+attr_latex: :options breaklines #+caption[Patch buffer local =ol-info=]: #+caption: Patch buffer local =ol-info=. #+name: lst:emacs-lisp-setup-buffer-local-ol-info #+begin_src emacs-lisp -n :exports code :results none :tangle no (with-eval-after-load 'ol-info (make-variable-buffer-local 'org-info-emacs-documents) (setq org-info-emacs-documents (delete "org" org-info-emacs-documents)) (make-variable-buffer-local 'org-info-other-documents) (setq org-info-other-documents (cl-union '(("consult" . "https://github.com/minad/consult") ("embark" . "https://github.com/oantolin/embark") ("magit" . "https://magit.vc/manual/magit.html") ("marginalia" . "https://github.com/minad/marginalia") ("maxima" . "https://maxima.sourceforge.io/docs/manual/maxima_singlepage.html") ("org" . "https://orgmode.org/org.html") ("orderless" . "https://github.com/oantolin/orderless") ("sly" . "https://joaotavora.github.io/sly/") ("vertico" . "https://github.com/minad/vertico")) org-info-other-documents :test #'equal))) #+end_src *** [[https://bitspook.in/blog/extending-org-mode-to-handle-youtube-links/][Extending org-mode to handle YouTube links]] :PROPERTIES: :CUSTOM_ID: sec:make-org-yt-link-type :END: Listing [[lst:define-org-yt-link-type][define org-yt-link type]] implements code to open the link to the following YouTube video [[yt:eaZUZCzaIgw][Extending org-mode to handle YouTube links]] and the following [[https://raw.githubusercontent.com/bitspook/spookmax.d/master/readme.org][Emacs setup]]. Opening [[yt:eaZUZCzaIgw][Extending org-mode to handle YouTube links]] may fail due to a bug in the interface between =mpv= and =yt-dlp=. Listing [[lst:set-emms-options][set EMMS options]] indicates how to work around this bug at the cost of making the user interface less clean. However, the link [[yt:48JlgiBpw_I][Absolute Beginner's Guide to Emacs]] works always. The following posts provide programming information: 1. [[https://developers.google.com/youtube/v3/docs][YouTube API reference]]. 2. [[https://developers.google.com/youtube/iframe_api_reference][YouTube Player API reference for iframe embeds]]. #+caption[Define an =org-link= type for =YouTube=]: #+caption: Define an =org-link= type for =YouTube=. #+name: lst:define-org-yt-link-type #+begin_src emacs-lisp -n :results silent ;; https://bitspook.in/blog/extending-org-mode-to-handle-youtube-paths/ (defun org-yt-emms-open (path) "Open an \"YouTube\" link with `emms' using \"mpv\"." (let ((url (format "https://www.youtube.com/watch?v=%s" path))) (require 'emms nil 'noerror) (emms-add-url url) (with-current-emms-playlist (save-excursion (emms-playlist-last) (emms-playlist-mode-play-current-track))))) (defun org-yt-url-browse-open (path) "Open an \"YouTube\" link with `url-browse'." (let ((url (format "https://www.youtube.com/watch?v=%s" path))) (browse-url url))) (defun org-yt-export (path desc backend _) "Export an \"YouTube\" link." (pcase backend (`html (format "" path desc)) (`latex (format "\\href{https://www.youtube.com/watch?v=%s}{%s}" path desc)) (_ path))) (with-eval-after-load 'ol (org-link-set-parameters "yt" :follow #'org-yt-emms-open :export #'org-yt-export)) #+end_src *** [[https://tecosaur.github.io/emacs-config/#translate-capital-keywords][Translate capital keywords (old) to lower case (new)]] :PROPERTIES: :CUSTOM_ID: sec:convert-upper-to-lower-case-keywords :END: #+caption[Convert upper to lower case keywords]: #+caption: Convert upper to lower case keywords. #+name: lst:convert-upper-to-lower-case-keywords #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs ;; https://tecosaur.github.io/emacs-config/#translate-capital-keywords (defun org-syntax-convert-keyword-case-to-lower () "Convert all #+KEYWORDS to #+keywords." (interactive) (when (derived-mode-p 'org-mode) (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 *** [[https://lists.gnu.org/archive/html/emacs-orgmode/2016-07/msg00394.html][Evaluate specific source blocks at load-time]] :PROPERTIES: :CUSTOM_ID: sec:load-time-specific-source-block-evaluation :END: [[https://emacs.stackexchange.com/questions/12938/how-can-i-evaluate-elisp-in-an-orgmode-file-when-it-is-opened][How to do load time source block evaluation]] #+caption[Evaluate specific source blocks at load-time]: #+caption: Evaluate specific source blocks at load-time. #+name: lst:load-time-specific-source-block-evaluation #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (defun org-eval-named-blocks-with-infix (infix) "Evaluate all source blocks having INFIX in their name." (when (eq major-mode 'org-mode) (let ((blocks (org-element-map (org-element-parse-buffer 'greater-element nil) 'src-block (lambda (block) (when-let ((name (org-element-property :name block))) (when (string-match-p infix name) block)))))) (dolist (block blocks) (goto-char (org-element-property :begin block)) (org-babel-execute-src-block))))) (defun org-eval-emacs-lisp-setup-blocks () "Evaluate all source blocks having \"emacs-lisp-setup\" in their name." (interactive) (org-eval-named-blocks-with-infix "emacs-lisp-setup")) (defun org-eval-python-setup-blocks () "Evaluate all source blocks having \"python-setup\" in their name." (interactive) (org-eval-named-blocks-with-infix "python-setup")) ;; Emacs looks for "Local variables:" after the last "newline-formfeed". (add-to-list 'safe-local-eval-forms '(org-eval-emacs-lisp-setup-blocks)) (add-to-list 'safe-local-eval-forms '(org-eval-python-setup-blocks))) #+end_src *** [[info:org#Macro Replacement][Org-mode macro utilities]] :PROPERTIES: :CUSTOM_ID: sec:org-macro-utilities :END: Listing [[lst:by-backend-kbd-org-macro]] defines the Emacs Lisp utilities to define the [[https://orgmode.org/][Org mode]] =kbd= macro in listing [[lst:source-file-export-keyword-settings]]. #+attr_latex: :options breaklines #+caption[Define Emacs Lisp utilities to define the =Org-mode= =kbd= macro]: #+caption: Define Emacs Lisp utilities to define the =Org-mode= =kbd= macro. #+name: lst:by-backend-kbd-org-macro #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (when (ensure-package-installation 'htmlize) (autoload 'htmlize-protect-string "htmlize" nil t)) ;; https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-LaTeX.html (defmacro by-backend (&rest body) "Help for org-export backend dependent execution." `(cl-case ',(bound-and-true-p org-export-current-backend) ,@body)) (defun by-backend-kbd-org-macro (keys) "Help for an org-export backend dependent \"#+macro: kbd\"." (by-backend (html (format "@@html:%s@@" (htmlize-protect-string keys))) (latex (format "@@latex:\\colorbox{PowderBlue}{\\texttt{%s}}@@" keys))))) #+end_src *** [[info:org#Export Settings][File inclusion (info)]] and [[info:org#Noweb Reference Syntax][Noweb (info)]] trickery :PROPERTIES: :CUSTOM_ID: sec:file-inclusion-and-noweb :END: Note: [[https://list.orgmode.org/87sfisf31o.fsf@posteo.net/][How to include LaTeX packages in a LaTeX export block]]. Listing [[lst:source-file-export-keyword-settings]] shows the preamble lines of this [[file:README.org]] file. It lists the export keyword settings, the definition of the [[https://orgmode.org/][Org mode]] =kbd= macro and the source block that generates part of the LaTeX preamble. Listing [[lst:latex-header-1]], [[lst:latex-header-2]], [[lst:latex-header-3]], [[lst:latex-header-4]], and [[lst:latex-header-5]] show the LaTeX source blocks that generate the LaTeX preamble. All listings in this section exists thanks to [[info:org#Export Settings][file inclusion (info)]] and [[info:org#Noweb Reference Syntax][noweb (info)]] trickery. #+caption[Source file export keyword settings]: #+caption: The preamble lines of README.org containing the export keyword #+caption: settings, the definition of the Org-mode =kbd= macro, and the #+caption: source block that generates part of the LaTeX preamble. #+name: lst:source-file-export-keyword-settings #+include: "README.org" src org -n :lines "1-17" #+name: latex-header-1 #+begin_src latex -n :exports none % Begin of the LaTeX preamble: % See: https://list.orgmode.org/87o807r7fr.fsf@posteo.net/ % From: "Juan Manuel Macías" % To: orgmode % Subject: [tip] Insert arbitrary LaTeX code at the beginning of any float environment % Date: Sun, 08 May 2022 22:22:16 +0000 % Message-ID: <87o807r7fr.fsf@posteo.net> % LuaLaTeX-, PdfLaTeX-, or XeTeX-COMPILER COMPATIBILITY: % Prevent collisions by using font packages before compiler specific packages. \usepackage{ifthen,ifluatex,ifxetex} \ifthenelse{\boolean{luatex}}{ \usepackage{fontspec} % lualatex }{\ifthenelse{\boolean{xetex}}{ \usepackage{mathspec} % xetex }{ \usepackage[T1]{fontenc} % pdflatex \usepackage[utf8]{inputenc} % pdflatex } } \usepackage{amsmath} \usepackage{amssymb} \usepackage{pifont} % check mark (\ding{52}) and cross mark (\ding{56}) \usepackage{textcomp} % \texttimes \usepackage{wasysym} % \diameter % Org-mode REQUIREMENTS: \usepackage{graphicx} \usepackage{longtable} \usepackage{wrapfig} \usepackage{rotating} \usepackage[normalem]{ulem} \usepackage{capt-of} \usepackage{hyperref} % End of the 1st LaTeX header block. #+end_src #+caption[LaTeX preamble: replacing the =Org-mode= default packages]: #+caption: LaTeX preamble: replacing the =Org-mode= default packages. #+name: lst:latex-header-1 #+begin_src latex -n :exports code :noweb yes <> #+end_src #+name: latex-header-2 #+begin_src latex -n :exports none % LANGUAGE: \usepackage{babel} \usepackage{fvextra} \usepackage{csquotes} % LISTS: \usepackage{enumitem} \setlist{noitemsep} % LISTINGS: % Section 2.6 of caption-eng.pdf (texdoc caption) explains that the sign % of "skip" depends on the assumption "position=above" or "position=below". % The assumption should match the real caption position in the LaTeX code. \usepackage{caption} \usepackage[newfloat]{minted} \captionsetup[listing]{position=below,skip=0em} \usemintedstyle{xcode} % TABLES: % https://tex.stackexchange.com/questions/341205/ % what-is-the-difference-between-tabular-tabular-and-tabularx-environments % https://emacs.stackexchange.com/questions/26179/ % change-org-mode-table-style-just-for-latex-export % https://tex.stackexchange.com/questions/468585/ % table-formatting-using-siunitx \usepackage{booktabs} \usepackage{colortbl} \usepackage{tabularx} % DANGER: beware of Org table :width and :align options! % End of the 2nd LaTeX header block. #+end_src #+caption[LaTeX preamble: language, lists and floats]: #+caption: LaTeX preamble: language, lists and floats. #+name: lst:latex-header-2 #+begin_src latex -n :exports code :noweb yes <> #+end_src #+name: latex-header-3 #+begin_src latex -n :exports none % PAGE LAYOUT: \usepackage{fancyhdr} \usepackage{lastpage} \usepackage[ headheight=20mm, top=40mm, bottom=20mm, left=0.1\paperwidth, right=0.1\paperwidth, heightrounded, verbose, ]{geometry} % TECHNICS: \usepackage{siunitx} \usepackage{tikz} % End of the 3rd LaTeX header block. #+end_src #+caption[LaTeX preamble: page layout and technics]: #+caption: LaTeX preamble: page layout. #+name: lst:latex-header-3 #+begin_src latex -n :exports code :noweb yes <> #+end_src #+name: latex-header-4 #+begin_src latex -n :exports none % FLOAT BARRIERS: % https://tex.stackexchange.com/questions/118662/use-placeins-for-subsections % Make section an implicit float barrier: \usepackage[section]{placeins} % Make subsection an implicit float barrier: \makeatletter \AtBeginDocument{% \expandafter\renewcommand\expandafter\subsection\expandafter{% \expandafter\@fb@secFB\subsection }% } \makeatother % Make subsubsection an implicit float barrier: \makeatletter \AtBeginDocument{% \expandafter\renewcommand\expandafter\subsubsection\expandafter{% \expandafter\@fb@secFB\subsubsection }% } % End of the 4th LaTeX header block. #+end_src #+caption[LaTeX preamble: float barriers]: #+caption: LaTeX preamble: float barriers. #+name: lst:latex-header-4 #+begin_src latex -n :exports code :noweb yes <> #+end_src #+name: latex-header-5 #+begin_src latex -n :exports none % FANCY HEADERS AND FOOTERS: % Add fancy headers and footers to normal pages. \pagestyle{fancy} \fancyhf{} \renewcommand{\footrulewidth}{0.4pt} \fancyfoot[C]{\emph{ Emacs for Go, \LaTeX{}, Lisp, Org, and Python -- Gerard Vermeulen } } \renewcommand{\headrulewidth}{0.4pt} \fancyhead[L]{\includegraphics[height=1.8cm]{Org-mode-unicorn.png}} \fancyhead[C]{ Page: \thepage/\pageref{LastPage} \\ \text{ } \\ \text{ } \\ DRAFT } \fancyhead[R]{\includegraphics[height=1.8cm]{Emacs-logo.png}} % Add fancy header and footer to custom titlepage. % https://tex.stackexchange.com/questions/506102/ % adding-header-and-footer-to-custom-titlepage \fancypagestyle{titlepage}{% \fancyhf{} \renewcommand{\footrulewidth}{0.4pt} \fancyfoot[C]{\emph{ Emacs for Go, \LaTeX{}, Lisp, Org, and Python -- Gerard Vermeulen } } \renewcommand{\headrulewidth}{0.4pt} \fancyhead[L]{\includegraphics[height=1.8cm]{Org-mode-unicorn.png}} \fancyhead[C]{ \pageref{LastPage} pages \\ \text{ } \\ \text{ } \\ DRAFT } \fancyhead[R]{\includegraphics[height=1.8cm]{Emacs-logo.png}} } % End of the 5th and last LaTeX header block. #+end_src #+caption[LaTeX preamble: fancy headers and footers]: #+caption: LaTeX preamble: fancy headers and footers. #+name: lst:latex-header-5 #+begin_src latex -n :exports code :noweb yes <> #+end_src *** [[info:org#LaTeX header and sectioning][Deprecated easy LaTeX preamble editing methods]] :PROPERTIES: :CUSTOM_ID: sec:easy-latex-preamble-editing :END: There are at least two deprecated (old and historic) ways to edit the LateX preamble =latex_header= and =latex_extra_header= export options easily in LaTeX source or export blocks. This [[info:org#Top][Org (info)]] file uses the recommended method of noweb trickery in Section [[#sec:file-inclusion-and-noweb]]. The old way -- exploiting an idea of [[https://www.matem.unam.mx/~omar/][Omar Antolin Camarena]] -- is to code new [[info:org#Editing Source Code][-modes]] allowing to edit in LaTeX mode and to export to LaTeX code with [[info:org#LaTeX specific export settings][correct LaTeX preamble export setting prefixes]]. Here, are links to three posts exposing his idea: 1. [[https://www.reddit.com/r/orgmode/comments/7u2n0h/tip_for_defining_latex_macros_for_use_in_both/][Export LaTeX macros to LaTeX and HTML/MathJax preambles (reddit)]], 2. [[https://www.reddit.com/r/orgmode/comments/5bi6ku/tip_for_exporting_javascript_source_block_to/][Export JavaScript source blocks to script tags in HTML (reddit)]], 3. [[https://emacs.stackexchange.com/questions/28301/export-javascript-source-block-to-script-tag-in-html-when-exporting-org-file-to][Export JavaScript source blocks to script tags in HTML (SX)]]. Listing [[lst:org-babel-latex-header-blocks]] implements this way by means of two new [[info:org#Editing Source Code][-modes]]: =latex-header= and =latex-extra-header=. #+caption[New =-modes= to edit the LaTeX preamble easily]: #+caption: Add =latex-header= and =latex-extra-header= language modes to edit #+caption: LaTeX preamble =latex_header= and =latex_extra_header= export options #+caption: easily. #+name: lst:org-babel-latex-header-blocks #+begin_src emacs-lisp -n :exports code :results silent (with-eval-after-load 'emacs (defun prefix-all-lines (prefix body) (with-temp-buffer (insert body) (string-insert-rectangle (point-min) (point-max) prefix) (buffer-string))) (defun org-babel-execute:latex-extra-header (body _params) "Execute a block of LaTeX extra header lines with org-babel. This function is called by `org-babel-execute-src-block' and prefixes all lines with \"#+latex_extra_header: \"." (prefix-all-lines "#+latex_extra_header: " body)) (defun org-babel-execute:latex-header (body _params) "Execute a block of LaTeX header lines with org-babel. This function is called by `org-babel-execute-src-block' and prefixes all lines with \"#+latex_header: \"." (prefix-all-lines "#+latex_header: " body)) (defvar org-babel-default-header-args:latex-extra-header '((:exports . "results") (:results . "raw"))) (defvar org-babel-default-header-args:latex-header '((:exports . "results") (:results . "raw"))) (with-eval-after-load 'org-src (setopt org-src-window-setup 'current-window) (add-to-list 'org-src-lang-modes '("latex-header" . latex)) (add-to-list 'org-src-lang-modes '("latex-extra-header" . latex)))) #+end_src The historic way is to use a special export attribute as in the function =org-latex-header-blocks-filter= in [[https://git.sr.ht/~bzg/org-contrib/tree/master/item/lisp/ox-extra.el][ox-extra.el]]. Apparently, nobody is using this broken function (broken, since it relies on support only in org-mode before =2014-11-11=). Listing [[lst:org-latex-header-blocks-filter]] proposes a fix for =org-latex-header-blocks-filter=. #+caption[Convert marked LaTeX export blocks to LaTeX header lines]: #+caption: Convert marked LaTeX export blocks to LaTeX header lines. #+name: lst:org-latex-header-blocks-filter #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'ox (defun 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))))) ;; Push the filter on the hook. (cl-pushnew #'org-latex-header-blocks-filter org-export-before-parsing-hook)) #+end_src This file uses the new most simple way, while keeping the deprecated ways for backwards compatibility. A practical difference is that new way source blocks (contrary to old way export blocks) do not work in [[info:org#Export Settings][#+SETUPFILE: ]], but only in [[info:org#Export Settings][#+INCLUDE: ]] files. *** [[info:org#LaTeX specific export settings][Advanced LaTeX export settings]] :PROPERTIES: :CUSTOM_ID: sec:advanced-latex-export-settings :END: Listing [[lst:ox-latex-emacs-lisp-setup]] initializes the buffer local variables =org-latex-classes=, =org-latex-title-command=, =org-latex-toc-command=, and =org-latex-subtitle-format=. Listing [[lst/title-page]] is a template to initialize =org-latex-title-command=. Type {{{kbd(C-h v org-latex-classes)}}}, {{{kbd(C-h v org-latex-subtitle-format)}}}, {{{kbd(C-h v org-latex-title-command)}}}, and {{{kbd(C-h v org-latex-toc-command)}}} to read how those variables control exporting from Org-mode to LaTeX. #+caption[Define buffer local =ox-latex= variables]: #+caption: Define buffer local =ox-latex= variables. #+header: :var title-page=lst/title-page #+name: lst:ox-latex-emacs-lisp-setup #+begin_src emacs-lisp -n :exports code :results none :tangle no (when (require 'ox-latex nil 'noerror) ;; https://emacs.stackexchange.com/questions/47347/ ;; customizing-org-latex-title-command-to-edit-title-page (make-variable-buffer-local 'org-latex-classes) (cl-pushnew '("article-local" "\\documentclass[11pt]{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}")) org-latex-classes :key #'car :test #'equal) (make-variable-buffer-local 'org-latex-title-command) (setq org-latex-title-command (concat title-page)) (make-variable-buffer-local 'org-latex-toc-command) (setq org-latex-toc-command " \\tableofcontents\\label{toc} \\listoflistings \\listoftables \\newpage ") (make-variable-buffer-local 'org-latex-subtitle-format) (setq org-latex-subtitle-format "")) #+end_src #+caption[Show a title-page example for =org-latex-title-command=]: #+caption: Show a title-page example for =org-latex-title-command=. #+name: lst/title-page #+begin_src latex -n :exports code :results silent :tangle no \begin{titlepage} %% https://tex.stackexchange.com/questions/506102/ %% adding-header-and-footer-to-custom-titlepage \thispagestyle{titlepage} \begin{center} %% Title \begin{Huge} {\bf %t} \\ \vspace{1em} \end{Huge} %% Author \begin{Large} {\bf %a} \\ \vspace{1em} \end{Large} \end{center} \end{titlepage} #+end_src *** [[https://orgmode.org/worg/dev/org-syntax-edited.html][Org Syntax]] :PROPERTIES: :CUSTOM_ID: sec:org-syntax :END: Two tools to grok how [[https://orgmode.org/worg/dev/org-element-api.html][Org mode parsing]] works are the [[https://orgmode.org/worg/dev/org-syntax-edited.html][Org Syntax]] specification and the [[http://xahlee.info/emacs/emacs/elisp_parse_org_mode.html][Org mode parser tutorial]]. The [[https://orgmode.org/worg/dev/org-element-api.html][Org element parsing API]] boils down to three functions: 1. The function [[https://orgmode.org/worg/dev/org-element-api.html#global][~org-element-parse-buffer~]] implements a fully recursive buffer parser that returns an abstract syntax tree. 2. The functions [[https://orgmode.org/worg/dev/org-element-api.html#local][~org-element-at-point~ and ~org-element-context~]] return information on the document structure around point either at the element level or at the object level in case of ~org-element-context~. Listing [[lst:grok-org-element-tree]] improves the [[http://xahlee.info/emacs/emacs/elisp_parse_org_mode.html][Org mode parser tutorial]] by defining interactive wrappers that pretty print the results of those non-interactive =org-element= functions to an =Emacs-lisp= buffer. #+caption[Grok how =org-element= parses your document]: #+caption: Grok how =org-element= parses your document. #+name: lst:grok-org-element-tree #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'org-element (when (require 'pp nil 'noerror) (defconst grok-org-output "*Grok Org Element Output*" "Grok Org output buffer name.") (defun grok-org-element-at-point () (interactive) (pp-display-expression (org-element-at-point) grok-org-output)) (defun grok-org-element-context () (interactive) (pp-display-expression (org-element-context) grok-org-output)) (defun grok-org-element-parse-buffer () (interactive) (let ((what (completing-read "granularity: " '(headline element greater-element object) nil 'require-match))) (pp-display-expression (org-element-parse-buffer what) grok-org-output))) (defun grok-org-heading-components () (interactive) (pp-display-expression (org-heading-components) grok-org-output)))) #+end_src ** Grammar, spelling, and style tools :PROPERTIES: :CUSTOM_ID: sec:writing-tools :END: *** [[info:emacs#Abbrevs][Abbrevs (info)]] :PROPERTIES: :CUSTOM_ID: sec:writing-abbreviations :END: [[https://www.masteringemacs.org/][Mickey Peterson]] has posted [[https://www.masteringemacs.org/article/correcting-typos-misspellings-abbrev][Correcting Typos and Misspellings with Abbrev]] showing how to use [[info:emacs#Keyboard Macros][Keyboard Macros (info)]] to exploit [[https://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines][Wikipedia's list of common misspellings for machines]]. Listing [[lst:misspellings-abbrev]] defines his keyboard macro under the name =misspellings-abrev=. I have used those directions to define src_emacs-lisp{global-abbrev-table} and to store it in src_emacs-lisp{abbrev-file-name} for git management. I can change the abbreviation definitions in this file by means of: 1. Execute src_emacs-lisp{(edit-abbrevs)} to alter abbreviation definitions by editing an =*Abbrevs*= buffer. 2. Add, edit, or remove definitions of the form ="source" 1 "target"= under the global or a mode-specific table. 3. Execute src_emacs-lisp{(abbrev-edit-save-buffer)} to save all user abbreviation definitions in the current buffer. #+caption[Definition of the =misspellings-abbrev= keyboard macro]: #+caption: Definition of the =misspellings-abbrev= keyboard macro. #+name: lst:misspellings-abbrev #+begin_src emacs-lisp -n :results silent (with-eval-after-load 'emacs (defun browse-common-misspellings () "Open the Wikipedia page of common misspellings for machines in EWW." (interactive) (eww (concat "https://en.wikipedia.org/wiki/Wikipedia" ":Lists_of_common_misspellings/For_machines"))) (fset 'misspellings-abbrev (kmacro-lambda-form [?\C-s ?- ?> return backspace backspace ?\C-k ?\C-x ?a ?i ?g ?\C-y return] 0 "%d")) (setq-default abbrev-mode t)) #+end_src Listing [[lst:word-games]] defines the =anagram-p= function that migth be used games. #+caption[Word games]: #+caption: Word games. #+name: lst:word-games #+begin_src emacs-lisp -n :results silent ;; https://funcall.blogspot.com/2022/07/lets-play-wordle.html ;; https://github.com/tabatkins/wordle-list (defun anagram-p (evil vile) "Check whether strings EVIL and VILE are anagrams of each other." (if (string= evil vile) nil (string= (cl-sort evil #'<) (cl-sort vile #'<)))) ;; https://funcall.blogspot.com/2020/01/palindromes-redux-and-sufficiently.html ;; https://irreal.org/blog/?p=8570 (defun palindrome-p (word) "Check whether the string WORD is a palindrome." (cl-do ((head 0 (1+ head)) (tail (1- (length word)) (1- tail)) (ok t)) ((or (not ok) (>= head tail) ok) (setq ok (= (aref word head) (aref word tail)))))) #+end_src *** [[https://dict.org/bin/Dict][DICT.org]] :PROPERTIES: :CUSTOM_ID: sec:writing-dict :END: Evaluating src_emacs-lisp{(dictionary)} connects to a local or remote =dictd= server. The following links explain how to configure and use =dictd=: 1. [[https://jpmens.net/2020/03/08/looking-up-words-with-dict/][Looking up words with DICT]] 2. [[https://www.rfc-editor.org/rfc/rfc2229.html][RFC 2229: A Dictionary Server Protocol]] 3. [[https://www.masteringemacs.org/article/wordsmithing-in-emacs][Wordsmithing in Emacs]] I am using the following Debian Bullseye dictionaries on Darwin and Gentoo: 1. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-devil/dict-devil_1.0-13.1_all.deb][dict-devil_1.0-13.1_all.deb]] 2. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-foldoc/dict-foldoc_20201018-1_all.deb][dict-foldoc_20201018-1_all.deb]] 3. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-gcide/dict-gcide_0.48.5+nmu1_all.deb][dict-gcide_0.48.5+nmu1_all.deb]] 4. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-jargon/dict-jargon_4.4.7-3.1_all.deb][dict-jargon_4.4.7-3.1_all.deb]] 5. [[http://ftp.fr.debian.org/debian/pool/main/v/vera/dict-vera_1.24-1_all.deb][dict-vera_1.24-1_all.deb]] 6. [[http://ftp.fr.debian.org/debian/pool/main/w/wordnet/dict-wn_3.0-36_all.deb][dict-wn_3.0-36_all.deb]] #+caption[Resource file for =dict= on =Darwin=]: #+caption: Resource file for =dict= on =Darwin=. #+name: lst:darwin-dict-resource-file #+header: :tangle (if (eq 'darwin system-type) "~/.dictrc" "no") #+begin_src conf -n # https://jpmens.net/2020/03/08/looking-up-words-with-dict/ server 127.0.0.1 { port 2628 } # Local Variables: # mode: conf-unix # End: #+end_src #+caption[Make a configuration file for =dictd= on =Darwin=]: #+caption: Make a configuration file for =dictd= on =Darwin=. #+name: lst:dictd-configuration-file #+begin_src shell -n :eval (if (eq 'darwin system-type) "yes" "never") :results silent cat > ~/.dictd.conf <