#+title: Emacs setup for use with LaTeX, Org, and Python #+author: Gerard Vermeulen #+latex_class: article #+latex_class_options: [11pt,a4paper,english,svgnames,tables] #+setupfile: "setup-include.org" #+include: "setup-include.org" * Quick start :PROPERTIES: :CUSTOM_ID: sec:quick-start :END: Backup your =user-emacs-directory= (defaults often to =~/.emacs.d= on =Linux=, =Unix=, or =Darwin=) directory and execute the commands in listing [[lst:prepare-user-emacs-directory]]. 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 :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 * 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]] requires [[info:org#Top][Org-9.5 (info)]], which is already part of Emacs-28.1. [[https://github.com/bdarcus/citar][Citar]] exploits the enhancements of Emacs' builtin selection mechanism provided by the extension packages [[https://github.com/minad/vertico][vertico]], [[https://github.com/oantolin/orderless][orderless]], [[https://github.com/oantolin/embark][embark]], [[https://github.com/minad/marginalia][marginalia]], and [[https://github.com/minad/consult][consult]]. The [[https://github.com/andras-simonyi/citeproc-el][citeproc]] extension package provides [[https://citationstyles.org/][CSL: citation style language]] processing capabilities to [[https://github.com/bdarcus/citar][citar]] and [[https://orgmode.org/][Org Mode]]. The [[https://github.com/vedang/pdf-tools][pdf-tools]] extension package renders [[https://en.wikipedia.org/wiki/PDF][PDF]] file with the possibility to annotate the file or to click on anchors in the [[https://en.wikipedia.org/wiki/PDF][PDF]] file that link back to the original [[https://www.latex-project.org/][LaTeX]] file of the document. An example of my work-flow are the steps to convert this [[info:org#Top][org]] file to [[https://en.wikipedia.org/wiki/PDF][PDF]] and to see the result with [[https://github.com/vedang/pdf-tools][pdf-tools]] in Emacs: execute the commands ~pdf-tools-install~, ~org-babel-tangle~, ~org-latex-export-latex-to-latex~, and ~compile~. This sets up an infinite [[https://www.latex-project.org/][LaTeX]] compilation loop to update and redisplay the [[https://en.wikipedia.org/wiki/PDF][PDF]] file after excution of the ~org-latex-export-latex-to-latex~ command in this buffer. Here follows a list of interesting Emacs configurations: 1. [[https://github.com/alhassy/emacs.d][Musa Al-hassy's configuration]] is an impressive example of producing the Emacs initialization files and other files by tangling an [[info:org#Top][org]] file. His methodology is impressive, as his [[https://alhassy.github.io/ElispCheatSheet/][Elisp Cheat Sheet]] and [[https://alhassy.github.io/org-special-block-extras/][org-special-block-extra package]] show. To me, this is a configuration to admire, but his methodology is way over my head. 2. [[https://github.com/oantolin/emacs-config][Omar Antolín Camarena's configuration]] exploits built-in packages, Omar's own small packages, and large external packages. Omar is the author of [[https://github.com/oantolin/orderless][orderless]] and [[https://github.com/oantolin/embark][embark]]. I have stolen his idea of using ~custom-set-variables~. 3. [[https://gitlab.com/ambrevar/dotfiles][Pierre Neirhardt's configuration]] implements lazy loading without help of external packages. I have stolen his approach of using lazy loading to silently ignore the setup stanzas of uninstalled extension packages. 4. [[https://sachachua.com/dotemacs/][Sacha Chua's configuration]] is a practical example of producing the Emacs initialization files by tangling an [[info:org#Top][org]] file. It gives me the impression that she is a very practical person trying to achieve her goals by the most efficient means. I have stolen her idea of using [[https://github.com/quelpa/quelpa][quelpa]] to install packages from any source. 5. [[https://github.com/purcell/emacs.d][Steve Purcell's configuration]] is well organized, a showcase of readable code, as well helpful commit and issue histories. See for instance the discussion on [[https://github.com/purcell/emacs.d/issues/778][the correctness of order of company candidates in Emacs lisp mode]]. * [[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 :tangle early-init.el ;;; early-init.el --- user early-init file -*- lexical-binding: t -*- ;;; Commentary: ;;; Code: (setq load-prefer-newer t) (require 'no-littering nil 'noerror) (provide 'early-init) ;; Emacs looks for "Local variables:" after the last "?\n?\f". ;; Local Variables: ;; indent-tabs-mode: nil ;; End: ;;; earl-init.el ends here #+end_src In order to get help in understanding the code block above in a buffer showing the original [[info:org#Top][Org]] source file, 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 customizes Emacs variables. It consists of three parts in listing [[lst:1st-custom-set-variables-call]], [[lst:2nd-custom-set-variables-call]], and [[lst:3rd-custom-set-variables-call]] in order to limit the length of the listings for exporting to LaTeX. The [[info:elisp#Quoting][quoting (info)]] and the [[info:elisp#Backquote][backquote (info)]] pages explain how to understand the reader macros ~'~ (quote), ~`~ (backquote), ~,~ (substitute) and ~@,~ (splice) in the ~custom-set-variables~ function calls in listing [[lst:1st-custom-set-variables-call]], [[lst:2nd-custom-set-variables-call]], and [[lst:3rd-custom-set-variables-call]]. A tutorial of how to use those reader macros is the [[https://mullikine.github.io/posts/macro-tutorial/][didactic emacs-lisp macro example]]. The [[info:emacs#Init File][init file (info)]] does not load the ~custom-file~ as [[info:emacs#Saving Customizations][saving customizations (info)]] recommends because of the ~custom-set-variables~ function calls. #+caption[Customize the first set of Emacs variables]: #+caption: Customize the first set of Emacs variables. #+name: lst:1st-custom-set-variables-call #+begin_src emacs-lisp ;;; init.el --- user init file -*- lexical-binding: t -*- ;;; Commentary: ;;; Code: (require 'cl-lib) (custom-set-variables '(after-save-hook #'executable-make-buffer-file-executable-if-script-p) '(column-number-mode t) '(cursor-type 'box) `(custom-file ,(locate-user-emacs-file "custom.el")) '(epg-pinentry-mode 'loopback) '(global-hl-line-mode t) '(global-hl-line-sticky-flag t) '(indent-tabs-mode nil) '(inhibit-startup-buffer-menu t) '(inhibit-startup-screen t) '(initial-buffer-choice t) '(initial-scratch-message "") `(insert-directory-program ,(or (executable-find "gls") (executable-find "ls"))) '(kill-ring-max 300) '(package-archives '(("gnu" . "https://elpa.gnu.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/") ("melpa" . "https://melpa.org/packages/"))) ;; Pin those packages to GNU ELPA to get the info documentation. '(package-pinned-packages '((consult . "gnu") (marginalia . "gnu") (vertico . "gnu")))) #+end_src #+caption[Customize the second set of Emacs variables]: #+caption: Customize the second set of Emacs variables. #+name: lst:2nd-custom-set-variables-call #+begin_src emacs-lisp (custom-set-variables `(package-selected-packages `(,@(when (version< emacs-version "28.0") '(org)) ; plain text thought organizer ,@(when (eq system-type 'darwin) '(applescript-mode)) ; mode to edit AppleScript code anaconda-mode ; strangles python-mode async ; asynchroneous processing auctex ; Aalborg University Center TeX blacken ; Black Python code formatter client citar ; bibliography handling citeproc ; bibliography handling company ; complete anything company-anaconda ; complete anything in anaconda-mode consult ; consult completing-read eglot ; Emacs polyGLOT LSP client electric-operator ; automatic spacing around operators elfeed ; web feed reader embark ; act on any buffer selection emms ; Emacs Multi-Media System htmlize ; convert buffer contents to HTML iedit ; simultaneous multi-entity editing laas ; LaTeX Auto-Activating Snippets magit ; Git Text-based User Interface marginalia ; minibuffer margin notes markdown-mode ; markdown text mode no-littering ; keep `user-emacs-directory' clean nov ; EPUB reader orderless ; Emacs completion style pdf-tools ; interactive docview replacement pdf-view-restore ; add view history to pdf-tools pyenv-mode ; Python environment selector quelpa ; install Emacs packages from source rainbow-mode ; set background color to color string sly ; Sylvester the Cat's Common Lisp IDE smartparens ; smart editing of character pairs toml-mode ; Tom's Obvious Minimal Language mode undo-tree ; more advanced yet simpler undo system vertico ; VERTical Interactive Completion wgrep ; open a writable grep buffer which-key ; on the fly key-binding help wordnut ; WordNet lexical database writegood-mode ; bullshit and weasel-word detector ws-butler ; remove trailing whitespace xr ; undo rx to grok regular expressions yasnippet))) ; code or text template expansion #+end_src #+caption[Customize the third set of Emacs variables]: #+caption: Customize the third set of Emacs variables. #+name: lst:3rd-custom-set-variables-call #+begin_src emacs-lisp (custom-set-variables '(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-answer t) '(view-read-only t)) (when (eq system-type 'darwin) (custom-set-variables '(ns-alternate-modifier nil) '(ns-command-modifier 'meta) '(ns-right-command-modifier 'super))) (when (eq window-system 'ns) (add-to-list 'initial-frame-alist '(height . 51)) (add-to-list 'initial-frame-alist '(width . 180))) #+end_src * [[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 three archives: 1. The [[https://elpa.gnu.org/][GNU Emacs Lisp Package Archive]] 2. The [[https://elpa.nongnu.org/][NonGNU Emacs Lisp Package Archive]]. 3. The [[https://melpa.org/#/][Milkypostman’s Emacs Lisp Package Archive (MELPA)]]. In addition, the [[https://github.com/quelpa/quelpa][quelpa]] tool allows to fetch code from any source and build a package on your computer before installation. The 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 ensures installation of [[https://github.com/quelpa/quelpa][quelpa]] before ensuring installation of [[https://www.reddit.com/r/emacs/comments/l6rcqh/undotree_repositorys_new_home_gitlab/][undo-tree]]. 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. 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 (unless (require 'no-littering nil 'noerror) (package-refresh-contents) (package-install 'no-littering) (require 'no-littering)) (unless (package-installed-p 'quelpa) (package-install 'quelpa)) (unless (package-installed-p 'undo-tree) ;; Neither GNU ELPA, nor MELPA have the latest version. (quelpa '(undo-tree :repo "tsc25/undo-tree" :fetcher gitlab))) (unless noninteractive (package-install-selected-packages)) #+end_src * [[info:dir#Top][Info documentation]] :PROPERTIES: :CUSTOM_ID: sec:info-documentation :END: Listing [[lst:configure-info]] fixes what looks like a bug in Emacs-28.0.91 and adds a path in my home directory to the places where =info= looks for files. #+caption[Configure =info=]: #+caption: Configure =info=. #+name: lst:configure-info #+begin_src emacs-lisp (with-eval-after-load 'info (unless (version< emacs-version "28.0") ;; Why is this necessary after `package-activate-1'? (dolist (item package-alist) (let ((pkg-dir (package-desc-dir (cadr item)))) (when (file-exists-p (expand-file-name "dir" pkg-dir)) (cl-pushnew pkg-dir Info-directory-list :test #'equal))))) ;; Emacs should 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: ** [[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 (setq disabled-command-function (defun my-enable-this-command (&rest _args) "Called when a disabled command is executed. Enable it and re-execute it." (put this-command 'disabled nil) (message "You typed %s. %s was disabled until now." (key-description (this-command-keys)) this-command) (sit-for 0) (call-interactively this-command))) #+end_src * [[info:emacs#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 (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 succesful (re)compilation on condition that the settings of the ~compile-command~ local variable in section are compatible. The local variable ~compile-command~ in the [[#sec:local-variables][local variables]] section (only visible in =org= files, but not in =html= and =pdf= files) shows how to use the =latexmkrc= file. #+attr_latex: :options breaklines #+caption[Tangle the Latexmk resource file]: #+caption: Tangle the Latexmk resource file. #+name: lst:latexmkrc #+begin_src perl :tangle latexmkrc :comments none # pdf previewer and update pdf previewer $pdf_previewer = "emacsclient -e '(find-file-other-window %S)'"; $pdf_update_method = 4; # 4 runs a command to force the update $pdf_update_command = "emacsclient -e '(with-current-buffer (find-buffer-visiting %S) (pdf-view-revert-buffer nil t))'"; # see for instance glossary.latexmkrc add_cus_dep( 'acn', 'acr', 0, 'makeglossaries' ); add_cus_dep( 'glo', 'gls', 0, 'makeglossaries' ); $clean_ext .= " acr acn alg bbl glo gls glg ist lol run.xml"; sub makeglossaries { my ($name, $path) = fileparse( $$Psource ); return system "makeglossaries -d '$path' '$name'"; } # Emacs looks for "Local variables:" after the last "?\n?\f". # Local Variables: # mode: perl # End: #+end_src ** [[https://qutebrowser.org/doc/userscripts.html][Qutebrowser userscript]] :PROPERTIES: :CUSTOM_ID: sec:qutebrowser-userscript :END: The next block contains an userscript that sends a [[info:org#The store-link protocol][store-link org-protocol]] message with the url and the title from [[https://qutebrowser.org][qutebrowser]] to =emacsclient=. The function =urlencode= translates the url and the title for the message. The [[info:python#Examples<22>][Python urllib examples]] show how to use =urlencode=. The final =execvp= call deals with a [[https://qutebrowser.org][qutebrowser]] userscript requirement: the =emacsclient= process must get the PID of the userscript that must kill itself after the take-over. Termination of the =emacsclient= process hands control back to [[https://qutebrowser.org][qutebrowser]]. On a [[https://en.wikipedia.org/wiki/POSIX][POSIX]] system, you can run the userscript from [[https://qutebrowser.org][qutebrowser]] or from a terminal to see whether it works. In case you try to run it from Emacs, Emacs may hang or die. #+caption[Tangle a qutebrowser userscript]: #+caption: Tangle a qutebrowser userscript. #+header: :comments none #+header: :tangle-mode (identity #o755) #+name: lst:qutebrowser-userscript #+begin_src python :noeval :tangle org-store-link #!/usr/bin/env python from urllib.parse import urlencode from os import environ, execvp url = environ.get("QUTE_URL", "https://orgmode.org") title = environ.get("QUTE_TITLE", "Org Mode") parameters = urlencode({"url": url, "title": title}) print(payload := f"org-protocol://store-link?{parameters}") execvp("emacsclient", ("-n", payload)) #+end_src ** TODO Look into: org-protocol handling with other browser on Darwin 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]] * Completion :PROPERTIES: :CUSTOM_ID: sec:completion :END: [[info:vertico#Top][Vertico (info)]] provides a performant and minimalistic vertical completion UI based on the default completion system and behaves therefore correctly under all circumstances. [[https://cestlaz.github.io/post/using-emacs-80-vertico/][Using Vertico, Marginalia, Consult, and Embark]] links to a video demonstration. Vertico integrates well with fully supported complementary packages to enrich the completion UI: 1. [[info:orderless#Top][Orderless (info)]] for an advanced completion style, 2. [[info:embark#Top][Embark (info)]] for minibuffer actions with context menus, 3. [[info:marginalia#Top][Marginalia (info)]] for rich annotations in the minibuffer, and 4. [[info:consult#Top][Consult (info)]] for useful search and navigation commands, where the order is that of [[https://github.com/bdarcus/citar#installation][enhancing citar's experience]] and the configuration steps below. Finally, [[https://company-mode.github.io/][company: a modular complete-anything framework for Emacs]] provides completion in any buffer and [[#sec:minibuffer-history-completion][minibuffer-history-completion]] provides completion on previous input in the minibuffer. ** [[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 (unless noninteractive (custom-set-variables '(history-delete-duplicates t) '(history-length 500) '(savehist-additional-variables '(eww-history kill-ring regexp-search-string search-ring search-string))) (savehist-mode +1) (when (fboundp 'vertico-mode) (vertico-mode +1)) (with-eval-after-load 'vertico (define-key vertico-map (kbd "RET") #'vertico-directory-enter) (define-key vertico-map (kbd "DEL") #'vertico-directory-delete-char) (define-key vertico-map (kbd "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 | keys | remap | |---------------------------------+---------------------+------------------------------------| | =vertico-directory-delete-char= | {{{kbd(DEL)}}} | | | =vertico-directory-delete-word= | {{{kbd(M-DEL)}}} | | | =vertico-directory-enter= | {{{kbd(RET)}}} | | | =vertico-exit= | {{{kbd(C-j)}}} | =exit-minibuffer= | | =vertico-exit-input= | {{{kbd(C-RET)}}} | | | =vertico-first= | {{{kbd(M-<)}}} | =beginning-of-buffer= | | =vertico-first= | {{{kbd(M-<)}}} | =minibuffer-beginning-of-buffer= | | =vertico-insert= | {{{kbd(TAB)}}} | | | =vertico-last= | {{{kbd(M->)}}} | =end-of-buffer= | | =vertico-next-group= | {{{kbd(C-)}}} | =forward-paragraph= | | =vertico-next= | {{{kbd()}}} | =next-line-or-history-element= | | =vertico-next= | {{{kbd(C-n)}}} | =next-line= | | =vertico-previous-group= | {{{kbd(C-)}}} | =backward-paragraph= | | =vertico-previous= | {{{kbd()}}} | =previous-line-or-history-element= | | =vertico-previous= | {{{kbd(C-p)}}} | =previous-line= | | =vertico-save= | {{{kbd(M-w)}}} | =kill-ring-save= | | =vertico-scroll-down= | {{{kbd(M-v)}}} | =scroll-down-command= | | =vertico-scroll-up= | {{{kbd(C-v)}}} | =scroll-up-command= | |---------------------------------+---------------------+------------------------------------| ** [[info:orderless#Top][Orderless (info)]] :PROPERTIES: :CUSTOM_ID: sec:orderless-configuration :END: Listing [[lst:configure-orderless]] enables =orderless=. #+caption[Configure =orderless=]: #+caption: Configure =orderless=. #+name: lst:configure-orderless #+begin_src emacs-lisp (unless noninteractive (when (fboundp 'orderless-filter) (custom-set-variables ;; https://github.com/purcell/emacs.d/issues/778 '(completion-styles '(basic completion-partial orderless)) '(completion-category-defaults nil) '(completion-category-overrides '((file (styles partial-completion))))) (add-hook 'minibuffer-setup-hook (defun my-on-minibuffer-setup-hook() (setq-default completion-styles '(substring orderless)))))) #+end_src ** [[info:embark#Top][Embark (info)]] :PROPERTIES: :CUSTOM_ID: sec:embark-configuration :END: Listing [[lst:configure-embark]] configures =embark=. #+caption[Configure =embark=]: #+caption: Configure =embark=. #+name: lst:configure-embark #+begin_src emacs-lisp (unless noninteractive (when (cl-every #'fboundp '(embark-act embark-bindings embark-dwim)) (global-set-key (kbd "C-,") #'embark-act) (global-set-key (kbd "C-:") #'embark-dwim) (global-set-key (kbd "C-h B") #'embark-bindings))) #+end_src ** [[info:marginalia#Top][Marginalia (info)]] :PROPERTIES: :CUSTOM_ID: sec:marginalia-configuration :END: 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 (unless noninteractive (when (fboundp 'marginalia-mode) (marginalia-mode +1))) #+end_src ** [[info:consult#Top][Consult (info)]] :PROPERTIES: :CUSTOM_ID: sec:consult-configuration :END: Listing [[lst:configure-consult]] configures =consult=. #+attr_latex: :booktabs yes :float table #+caption[Configuration specific key bindings]: #+caption: Configuration specific key-bindings. #+name: tab:configuration-specific-key-bindings |-------------------------------+---------------------+------------------------| | command | keys | key map | |-------------------------------+---------------------+------------------------| | =consult-apropos= | {{{kbd( a)}}} | =global-map= | | =consult-bookmark= | {{{kbd(C-x r b)}}} | =ctl-x-keymap= | | =consult-buffer-other-frame= | {{{kbd(C-x 5 b)}}} | =ctl-x-keymap= | | =consult-buffer-other-window= | {{{kbd(C-x 4 b)}}} | =ctl-x-keymap= | | =consult-buffer= | {{{kbd(C-x b)}}} | =ctl-x-keymap= | | =consult-compile-error= | {{{kbd(M-g e)}}} | =goto-map= | | =consult-complex-command= | {{{kbd(C-x M-:)}}} | =ctl-x-keymap= | | =consult-find= | {{{kbd(M-s f)}}} | =search-map= | | =consult-focus-lines= | {{{kbd(M-s u)}}} | =search-map= | | =consult-git-grep= | {{{kbd(M-s g)}}} | =search-map= | | =consult-global-mark= | {{{kbd(M-g k)}}} | =goto-map= | | =consult-goto-line= | {{{kbd(M-g M-g)}}} | =goto-map= | | =consult-goto-line= | {{{kbd(M-g g)}}} | =goto-map= | | =consult-history= | {{{kbd(C-c h)}}} | =global-map= | | =consult-imenu-project= | {{{kbd(M-g i)}}} | =goto-map= | | =consult-keep-lines= | {{{kbd(M-s k)}}} | =search-map= | | =consult-line= | {{{kbd(M-s l)}}} | =search-map= | | =consult-mark= | {{{kbd(M-g m)}}} | =goto-map= | | =consult-mode-command= | {{{kbd(C-c m)}}} | =global-map= | | =consult-multi-occur= | {{{kbd(M-s m)}}} | =search-map= | | =consult-outline= | {{{kbd(M-g o)}}} | =goto-map= | | =consult-register= | {{{kbd(C-x r x)}}} | =ctl-x-keymap= | | =consult-yank-pop= | {{{kbd(M-y)}}} | =global-map= | |-------------------------------+---------------------+------------------------| | =elfeed= | {{{kbd(C-x w)}}} | =global-map= | | =embark-act= | {{{kbd(C-\,)}}} | =global-map= | | =embark-bindings= | {{{kbd(C-h B)}}} | =global-map= | | =embark-dwim= | {{{kbd(C-:)}}} | =global-map= | | =iedit-mode= | {{{kbd(C-;)}}} | =global-map= | | =minibuffer-complete-history= | {{{kbd(C-)}}} | =minibuffer-local-map= | | =narrow-or-widen-dwim= | {{{kbd(C-x C-n)}}} | =ctl-x-keymap= | | =org-agenda= | {{{kbd(C-c a)}}} | =global-map= | | =org-capture= | {{{kbd(C-c c)}}} | =global-map= | | =org-cite= | {{{kbd(C-c b)}}} | =org-mode-map= | | =org-insert-link-global= | {{{kbd(C-c C-l)}}} | =global-map= | | =org-narrow-to-table= | {{{kbd(C-x n t)}}} | =ctl-x-keymap= | | =org-store-link= | {{{kbd(C-c l)}}} | =global-map= | |-------------------------------+---------------------+------------------------| #+caption[Configure =consult=]: #+caption: Configure =consult=. #+name: lst:configure-consult #+begin_src emacs-lisp (unless noninteractive (when (fboundp 'consult-apropos) (custom-set-variables '(consult-project-root-function #'vc-root-dir)) ;; C-c bindings (mode-specific-map) (global-set-key (kbd "C-c h") #'consult-history) (global-set-key (kbd "C-c m") #'consult-mode-command) ;; C-x bindings (ctl-x-map) (define-key ctl-x-map (kbd "M-:") #'consult-complex-command) (define-key ctl-x-map (kbd "b") #'consult-buffer) (define-key ctl-x-map (kbd "4 b") #'consult-buffer-other-window) (define-key ctl-x-map (kbd "5 b") #'consult-buffer-other-frame) (define-key ctl-x-map (kbd "r x") #'consult-register) (define-key ctl-x-map (kbd "r b") #'consult-bookmark) ;; M-g bindings (goto-map) (define-key goto-map (kbd "g") #'consult-goto-line) (define-key goto-map (kbd "M-g") #'consult-goto-line) (define-key goto-map (kbd "o") #'consult-outline) (define-key goto-map (kbd "m") #'consult-mark) (define-key goto-map (kbd "k") #'consult-global-mark) (define-key goto-map (kbd "i") #'consult-imenu-project) (define-key goto-map (kbd "e") #'consult-compile-error) ;; M-s bindings (search-map) (define-key search-map (kbd "g") #'consult-git-grep) (define-key search-map (kbd "f") #'consult-find) (define-key search-map (kbd "k") #'consult-keep-lines) (define-key search-map (kbd "l") #'consult-line) (define-key search-map (kbd "m") #'consult-multi-occur) (define-key search-map (kbd "u") #'consult-focus-lines) ;; Other bindings (global-set-key (kbd "M-y") #'consult-yank-pop) (global-set-key (kbd " a") #'consult-apropos) ;; Tweak functions (advice-add 'completing-read-multiple :override #'consult-completing-read-multiple) (fset 'multi-occur #'consult-multi-occur))) #+end_src ** [[https://company-mode.github.io/][Company: a modular complete anything framework for Emacs]] :PROPERTIES: :CUSTOM_ID: sec:company-configuration :END: Listing [[lst:configure-company]] configures =company=. #+caption[Configure =company=]: #+caption: Configure =company=. #+name: lst:configure-company #+begin_src emacs-lisp (unless noninteractive (when (fboundp 'company-mode) (custom-set-variables ;; https://github.com/purcell/emacs.d/issues/778 '(company-transformers '(company-sort-by-occurrence))) (dolist (hook '(LaTeX-mode-hook org-mode-hook emacs-lisp-mode-hook lisp-interaction-mode-hook lisp-mode-hook python-mode-hook ielm-mode-hook sly-mrepl-mode-hook)) (add-hook hook #'company-mode)))) #+end_src ** [[https://lists.gnu.org/archive/html/emacs-devel/2021-12/msg00802.html][Minibuffer history completion]] :PROPERTIES: :CUSTOM_ID: sec:minibuffer-history-completion :END: See [[https://lists.gnu.org/archive/html/emacs-devel/2021-12/msg00802.html][Juri Linkov (Emacs Developer mailing list)]] for how to allow completion on previous input in the minibuffer. Listing [[lst:enable-minibuffer-history-completion]] enables minibuffer history completion. #+caption[Enable =minibuffer-history-completion=]: #+caption: Enable =minibuffer-history-completion=. #+name: lst:enable-minibuffer-history-completion #+begin_src emacs-lisp (defun minibuffer-setup-history-completions () (unless (or minibuffer-completion-table minibuffer-completion-predicate) (setq-local minibuffer-completion-table (symbol-value minibuffer-history-variable)))) (add-hook 'minibuffer-setup-hook 'minibuffer-setup-history-completions) ;; Stolen from Emacs-28.1 for Emacs-27.2: (unless (fboundp 'minibuffer--completion-prompt-end) (defun minibuffer--completion-prompt-end () (let ((end (minibuffer-prompt-end))) (if (< (point) end) (user-error "Can't complete in prompt") end)))) ;; Adapted from ‘minibuffer-complete’: (defun minibuffer-complete-history () "Allow minibuffer completion on previous input." (interactive) (completion-in-region (minibuffer--completion-prompt-end) (point-max) (symbol-value minibuffer-history-variable) nil)) (define-key minibuffer-local-map (kbd "C-") #'minibuffer-complete-history) #+end_src ** [[https://github.com/justbur/emacs-which-key#readme][Prefix key-binding help]] :PROPERTIES: :CUSTOM_ID: sec:prefix-key-binding-help :END: Configure ~which-key-mode~ so that typing =C-h= after a prefix key displays all keys available after the prefix key. Listing [[lst:enable-which-key-mode]] enables [[https://github.com/justbur/emacs-which-key#manual-activation][manual activation]] of =which-key-mode=. This uses =prefix-help-command= behind the scenes and is therefore incompatible with straightforward use of [[info:embark#Quick start][=embark-prefix-help-command= (info)]]. #+caption[Enable =which-key-mode=]: #+caption: Enable =which-key-mode=. #+name: lst:enable-which-key-mode #+begin_src emacs-lisp (when (fboundp 'which-key-mode) (custom-set-variables '(which-key-idle-delay 10000) '(which-key-idle-secondary-delay 0.05) '(which-key-show-early-on-C-h t)) (which-key-mode +1)) #+end_src * Reading :PROPERTIES: :CUSTOM_ID: sec:reading :END: ** Reading EPUB files :PROPERTIES: :CUSTOM_ID: sec:reading-epub-files :END: Listing [[lst:enable-nov-mode]] enables =nov-mode=. #+caption[Enable =nov-mode=]: #+caption: Enable =nov-mode=. #+name: lst:enable-nov-mode #+begin_src emacs-lisp (when (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. In order to use [[https://github.com/vedang/pdf-tools][pdf-tools]], you have to type =M-x pdf-tools-install= after installation of [[https://github.com/vedang/pdf-tools][pdf-tools]] from [[https://melpa.org/][MELPA]] or after each update of [[https://github.com/freedesktop/poppler][poppler]] to build or rebuild the =epdfinfo= executable that serves the [[https://en.wikipedia.org/wiki/PDF][PDF]] files to Emacs. #+caption[Enable =pdf-tools=]: #+caption: Enable =pdf-tools=. #+name: lst:enable-pdf-tools #+begin_src emacs-lisp ;; 'pdf-loader-install' is the lazy equivalent of 'pdf-tools-install': ;; see the README file. (when (fboundp 'pdf-loader-install) (pdf-loader-install)) (with-eval-after-load 'pdf-view (when (fboundp 'pdf-view-restore-mode) (add-hook 'pdf-view-mode-hook #'pdf-view-restore-mode))) #+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]]. For instance, the ~TeX-master~ safe local variable in the =tex.el= elisp library file has no autoload cookie. Without prior loading of =tex.el=, Emacs will complain that ~TeX-master~ is no safe local variable in case it reads a LaTeX file that sets ~TeX-master~. Listing [[lst:require-auctex]] initializes [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] properly. #+caption[Require =AUCTeX=]: #+caption: Require =AUCTeX=. #+name: lst:require-auctex #+begin_src emacs-lisp ;; Use `require' to make `TeX-master' a safe local variable. (when (require 'tex nil 'noerror) (custom-set-variables '(TeX-auto-save t) '(TeX-install-font-lock #'font-latex-setup) '(TeX-parse-self t))) #+end_src Although, the LaTeX =biblatex= is in use, listing [[lst:configure-bibtex]] configures the Emacs =bibtex= library for the LaTeX =BiBTeX= package to maintain backwards compatibility. #+caption[Configure =bibtex=]: #+caption: Configure =bibtex=. #+name: lst:configure-bibtex #+begin_src emacs-lisp (with-eval-after-load 'bibtex (custom-set-variables '(bibtex-dialect 'BibTeX))) #+end_src Listing [[lst:configure-font-latex]] disables font scaling of section titles. #+caption[Configure =font-latex=]: #+caption: Configure =font-latex=. #+name: lst:configure-font-latex #+begin_src emacs-lisp (with-eval-after-load 'font-latex (custom-set-variables '(font-latex-fontify-sectioning 1.0))) #+end_src Listing [[lst:configure-latex]] configures =latex= for a full featured =LaTeX-section-command=. #+caption[Configure =latex=]: #+caption: Configure =latex=. #+name: lst:configure-latex #+begin_src emacs-lisp (with-eval-after-load 'latex (custom-set-variables '(LaTeX-section-hook '(LaTeX-section-heading LaTeX-section-title LaTeX-section-toc LaTeX-section-section LaTeX-section-label)))) #+end_src Out of the box, [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] does not indent text between square brackets. The code in listing [[lst:configure-tex]] corrects this by advising to override ~TeX-brace-count-line~ with ~my-TeX-brace-count-line~. #+caption[Configure =TeX=]: #+caption: Configure =TeX=. #+name: lst:configure-tex #+begin_src emacs-lisp (with-eval-after-load 'tex (custom-set-variables ;; Disable `TeX-electric-math' to prevent collisions with `smartparens'. '(TeX-electric-math nil)) ;; https://emacs.stackexchange.com/questions/17396/ ;; indentation-in-square-brackets (defun my-TeX-brace-count-line () "Count number of open/closed braces." (save-excursion (let ((count 0) (limit (line-end-position)) char) (while (progn (skip-chars-forward "^{}[]\\\\" limit) (when (and (< (point) limit) (not (TeX-in-comment))) (setq char (char-after)) (forward-char) (cond ((eq char ?\{) (setq count (+ count TeX-brace-indent-level))) ((eq char ?\}) (setq count (- count TeX-brace-indent-level))) ((eq char ?\[) (setq count (+ count TeX-brace-indent-level))) ((eq char ?\]) (setq count (- count TeX-brace-indent-level))) ((eq char ?\\) (when (< (point) limit) (forward-char) t)))))) count))) (advice-add 'TeX-brace-count-line :override #'my-TeX-brace-count-line)) #+end_src *** TODO Improve the AUCTeX configuration slowly [[https://github.com/thisirs/dotemacs/blob/master/lisp/init-auctex.el][AUCTeX setup of an experienced user]] ** Writing Org files :PROPERTIES: :CUSTOM_ID: sec:writing-org-files :END: *** [[info:org#Activation][Org activation (info)]] :PROPERTIES: :CUSTOM_ID: sec:activate-org :END: #+caption[Activate =Org=]: #+caption: Activate =Org=. #+name: lst:activate-org #+begin_src emacs-lisp ;; Inspect: ;; function with "C-h f" ;; symbols with "C-h o" ;; variables with "C-h v" (global-set-key (kbd "C-c a") #'org-agenda) (global-set-key (kbd "C-c c") #'org-capture) (global-set-key (kbd "C-c l") #'org-store-link) (global-set-key (kbd "C-c C-l") #'org-insert-link-global) #+end_src *** Org customization :PROPERTIES: :CUSTOM_ID: sec:customize-org :END: The code in listing [[lst:customize-org-babel]], [[lst:customize-org]], and [[lst:customize-org-export]] does basic customization of [[https://orgmode.org/][Org mode]] variables. #+caption[Customize =org-babel=]: #+caption: Customize =org-babel=. #+name: lst:customize-org-babel #+begin_src emacs-lisp (with-eval-after-load 'ob-core (custom-set-variables '(org-confirm-babel-evaluate nil))) (with-eval-after-load 'ob-python (custom-set-variables '(org-babel-python-command "python -E"))) (with-eval-after-load 'ob-latex (custom-set-variables '(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 (with-eval-after-load 'sly (custom-set-variables '(org-babel-lisp-eval-fn #'sly-eval)))) #+end_src #+caption[Customize =Org=]: #+caption: Customize =Org=. #+name: lst:customize-org #+begin_src emacs-lisp (with-eval-after-load 'org (custom-set-variables '(org-babel-load-languages '((C . t) (calc . t) (dot . t) (emacs-lisp . t) (eshell . t) (fortran . t) (gnuplot . t) (latex . t) (lisp . t) (maxima . t) (org . t) (perl . t) (python . t) (shell . t))) '(org-export-backends '(ascii beamer html icalendar latex 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)) '(org-structure-template-alist '(("a" . "export ascii") ("c" . "center") ("C" . "comment") ("e" . "example") ("E" . "export") ("h" . "export html") ("l" . "export latex") ("q" . "quote") ("s" . "src") ("p" . "src python :session :async") ("v" . "verse"))))) #+end_src #+caption[Configure =org-mode-map=]: #+caption: Configure =org-mode-map=. #+name: lst:configure-org-mode-map #+begin_src emacs-lisp (with-eval-after-load 'org ;; 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))) (define-key org-mode-map (kbd "$") #'org-electric-dollar) (define-key org-mode-map (kbd "M-q") #'org-fill-paragraph)) #+end_src #+caption[Customize =org-export=]: #+caption: Customize =org-export=. #+name: lst:customize-org-export #+begin_src emacs-lisp (with-eval-after-load 'ox-latex (custom-set-variables '(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-listings 'minted) '(org-latex-minted-langs '((cc "c++") (cperl "perl") (diff "diff") (shell-script "bash") (org "text") (toml "toml"))) '(org-latex-minted-options '(("bgcolor" "LightGoldenrodYellow"))) `(org-latex-logfiles-extensions ',(cl-union '("lof" "lot") org-latex-logfiles-extensions :test #'equal)) '(org-latex-prefer-user-labels t) '(org-latex-subtitle-separate t))) #+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, orderless, 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. Listing [[lst:configure-oc-cite+citar]] configures =org-cite=, =citar=, and =org=. #+caption[Configure =org-cite= with =citar=]: #+caption: Configure =oc-cite= with =citar=. #+name: lst:configure-oc-cite+citar #+begin_src emacs-lisp (with-eval-after-load 'oc (require 'oc-biblatex) (require 'oc-csl) (custom-set-variables '(org-cite-export-processors '((latex biblatex) (t csl))) '(org-cite-global-bibliography '("~/VCS/research/refs.bib"))) (when (require 'citar nil 'noerror) (custom-set-variables '(org-cite-activate-processor 'citar) '(org-cite-follow-processor 'citar) '(org-cite-insert-processor 'citar)))) (with-eval-after-load 'org (when (require 'citar nil 'noerror) (custom-set-variables '(citar-bibliography '("~/VCS/research/refs.bib")) '(citar-file-extensions '("djvu" "pdf")) '(citar-library-paths '("~/VCS/research/papers/")))) (define-key org-mode-map (kbd "C-c b") #'org-cite-insert)) #+end_src *** 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]] *** [[info:org#Adding Hyperlink Types][Making Org hyperlink types (info)]] :PROPERTIES: :CUSTOM_ID: sec:making-org-hyperlink-types :END: The code in listing [[lst:define-org-link-types]] defines =org-link= types for backwards compatibility with [[https://github.com/jkitchin/org-ref][org-ref]]. #+caption[Define =org-link= types]: #+caption: Define =org-link= types. #+name: lst:define-org-link-types #+begin_src emacs-lisp (with-eval-after-load 'ol (org-link-set-parameters "ac*" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\gls*{%s}" path)) (_ path)))) (org-link-set-parameters "cite" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\cite{%s}" path)) (_ path)))) (org-link-set-parameters "eqref" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\eqref{%s}" path)) (_ path)))) (org-link-set-parameters "hyperlink" :export (lambda (path desc backend _info) (pcase backend (`latex (format "\\hyperlink{%s}{%s}" path desc)) (_ path)))) (org-link-set-parameters "label" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\label{%s}" path)) (_ path)))) (org-link-set-parameters "ref" :export (lambda (path _desc backend _info) (pcase backend (`latex (format "\\ref{%s}" path)) (_ path))))) #+end_src Listing [[lst:emacs-lisp-setup-patch-ol-info]] patches the function =org-info-export= and the constant ~org-info-other-documents~, to export the [[info:org#External Links][info: links (info)]] in this document to =html= and LaTeX correctly. #+caption[Patch =ol-info=]: #+caption: Patch =ol-info=. #+name: lst:emacs-lisp-setup-patch-ol-info #+begin_src emacs-lisp :exports code :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)))) (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") ("marginalia" . "https://github.com/minad/marginalia") ("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://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 (with-eval-after-load 'org (defun org-syntax-convert-keyword-case-to-lower () "Convert all #+KEYWORDS to #+keywords." (interactive) (save-excursion (goto-char (point-min)) (let ((count 0) (case-fold-search nil)) (while (re-search-forward "^[ \t]*#\\+[A-Z_]+" nil t) (unless (s-matches-p "RESULTS" (match-string 0)) (replace-match (downcase (match-string 0)) t) (setq count (1+ count)))) (message "Replaced %d keywords" count))))) #+end_src *** [[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 (defun my-org-eval-blocks-named (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) (let ((name (org-element-property :name block))) (when (and name (string-match-p infix name)) block)))))) (dolist (block blocks) (goto-char (org-element-property :begin block)) (org-babel-execute-src-block))))) ;; Emacs looks for "Local variables:" after the last "?\n?\f". (add-to-list 'safe-local-eval-forms '(apply 'my-org-eval-blocks-named '("emacs-lisp-setup"))) (add-to-list 'safe-local-eval-forms '(apply 'my-org-eval-blocks-named '("python-setup"))) #+end_src *** [[info:org#LaTeX header and sectioning][Easy LaTeX preamble editing]] :PROPERTIES: :CUSTOM_ID: sec:easy-latex-preamble-editing :END: There are at least two ways (new and old) 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 new way, but keeps the old way for backwards compatibility. The new 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:emacs-lisp-setup-latex-header]] 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:emacs-lisp-setup-latex-header #+begin_src emacs-lisp :exports code :silent (with-eval-after-load 'org-src (defun prefix-all-lines (prefix body) (with-temp-buffer (insert body) (string-insert-rectangle (point-min) (point-max) prefix) (buffer-string))) (add-to-list 'org-src-lang-modes '("latex-header" . latex)) (defvar org-babel-default-header-args:latex-header '((:exports . "results") (:results . "raw"))) (defun org-babel-execute:latex-header (body _params) "Execute a block of LaTeX preamble 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)) (add-to-list 'org-src-lang-modes '("latex-extra-header" . latex)) (defvar org-babel-default-header-args:latex-extra-header '((:exports . "results") (:results . "raw"))) (defun org-babel-execute:latex-extra-header (body _params) "Execute a block of LaTeX preamble 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))) #+end_src The old 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 (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)))))) #+end_src This file uses the new way, while keeping the old way for backwards compatibility, because the new way feels less hackish than the old way. 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#Export Settings][#+SETUPFILE: and #+INCLUDE: usage]] :PROPERTIES: :CUSTOM_ID: sec:setupfile+include-usage :END: Evaluation of the source block in listing [[lst:make-source-block-with-export-keyword-settings]] produces the source block that exports to listing [[lst:source-file-export-keyword-settings]] which shows the first six lines of this [[file:README.org]] file. The last two lines show that [[file:setup-include.org][setup-include.org]] is the argument for [[info:org#Export Settings][#+SETUPFILE:]] *and* [[info:org#Export Settings][#+INCLUDE:]]. #+caption[Make setup and include file export keyword settings source block]: #+caption: The shell script to make the source block containing the export #+caption: keyword settings. #+name: lst:make-source-block-with-export-keyword-settings #+begin_src shell :exports both :results drawer echo "#+caption[Source file export keyword settings]:" echo "#+caption: The first six lines of README.org containing the export" echo "#+caption: keyword settings." echo "#+name: lst:source-file-export-keyword-settings" echo "#+begin_src org :tangle no" head -n 6 README.org echo -n "#+end_src" #+end_src #+RESULTS: lst:make-source-block-with-export-keyword-settings :results: #+caption[Source file export keyword settings]: #+caption: The first six lines of README.org containing the export #+caption: keyword settings. #+name: lst:source-file-export-keyword-settings #+begin_src org :tangle no #+title: Emacs setup for use with LaTeX, Org, and Python #+author: Gerard Vermeulen #+latex_class: article #+latex_class_options: [11pt,a4paper,english,svgnames,tables] #+setupfile: "setup-include.org" #+include: "setup-include.org" #+end_src :end: Listing [[lst:setup-include-export-keyword-settings]] tangles into the [[file:setup-include.org][setup-include.org]] file and listing [[lst:by-backend-kbd-org-macro]] defines the tools for the [[https://orgmode.org/][Org mode]] =kbd= macro in [[file:setup-include.org][setup-include.org]]. #+caption[Setup and include file export keyword settings]: #+caption: Setup and include file export keyword settings. #+name: lst:setup-include-export-keyword-settings #+begin_src org ,#+babel: :cache no ,#+macro: kbd (eval (by-backend-kbd-org-macro $1)) ,#+options: ^:{} ,#+property: header-args:emacs-lisp :exports code :results silent :tangle init.el ,#+property: header-args:org :tangle setup-include.org ,#+startup: content #+end_src #+caption[Define the tools of the org-mode =kbd= macro]: #+caption: Define the tools for the org-mode =kbd= macro. #+name: lst:by-backend-kbd-org-macro #+begin_src emacs-lisp (with-eval-after-load 'ox (autoload 'htmlize-protect-string "htmlize" nil t) (defmacro by-backend (&rest body) `(cl-case org-export-current-backend ,@body)) (defun by-backend-kbd-org-macro (keys) (by-backend (html (format "@@html:%s@@" (htmlize-protect-string keys))) (latex (format "@@latex:\\colorbox{PowderBlue}{\\texttt{%s}}@@" keys))))) #+end_src Listing [[lst:use-latex-header-1]], [[lst:use-latex-header-2]], [[lst:use-latex-header-3]], and [[lst:use-latex-header-4]] tangle into the [[file:setup-include.org][setup-include.org]] file in order to create the [[https://www.latex-project.org/][LaTeX]] preamble. #+caption[LaTeX preamble: language, lists and floats]: #+caption: LaTeX preamble: language, lists and floats. #+name: lst:use-latex-header-1 #+begin_src org ,#+begin_src latex-header % 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_src #+end_src #+caption[LaTeX preamble: page layout]: #+caption: LaTeX preamble: page layout. #+name: lst:use-latex-header-2 #+begin_src org ,#+begin_src latex-header % PAGE LAYOUT: \usepackage{fancyhdr} \usepackage{lastpage} \usepackage[ headheight=20mm, top=40mm, bottom=20mm, left=60pt, right=60pt, heightrounded, verbose, ]{geometry} % TECHNICS: \usepackage{siunitx} \usepackage{tikz} ,#+end_src #+end_src #+caption[LaTeX preamble: float barriers]: #+caption: LaTeX preamble: float barriers. #+name: lst:use-latex-header-3 #+begin_src org ,#+begin_src latex-header % 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 }% } \makeatother ,#+end_src #+end_src #+caption[LaTeX preamble: fancy headers and footers]: #+caption: LaTeX preamble: fancy headers and footers. #+name: lst:use-latex-header-4 #+begin_src org ,#+begin_src latex-header % FANCY HEADERS AND FOOTERS: % Add fancy headers and footers to normal pages. \pagestyle{fancy} \fancyhf{} \renewcommand{\footrulewidth}{0.4pt} \fancyfoot[C]{\emph{ Emacs setup for use with \LaTeX{}, 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 setup for use with \LaTeX{}, 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}} } % #+latex_header: END. ,#+end_src #+end_src *** [[info:org#LaTeX specific export settings][Advanced LaTeX export settings]] :PROPERTIES: :CUSTOM_ID: sec:advanced-latex-export-settings :END: [[https://emacs.stackexchange.com/questions/47347/customizing-org-latex-title-command-to-edit-title-page][How to customize =org-latex-title-command= only in this buffer]] #+caption[Define =my-ox-latex-export-buffer-local-variables=]: #+caption: Define =my-ox-latex-export-buffer-local-variables=. #+name: lst:emacs-lisp-setup-defun #+begin_src emacs-lisp :results silent (defun my-ox-latex-export-buffer-local-variables (title-page) (with-eval-after-load 'ox (make-variable-buffer-local 'org-export-before-parsing-hook) (cl-pushnew #'org-latex-header-blocks-filter org-export-before-parsing-hook)) (when (require 'ox-latex nil 'noerror) (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 (mapconcat 'identity '("" "\\tableofcontents\\label{toc}" "\\listoffigures" "\\listoflistings" "\\listoftables" "\\newpage" "") "\n")) (make-variable-buffer-local 'org-latex-subtitle-format) (setq org-latex-subtitle-format ""))) #+end_src Listing [[lst:emacs-lisp-setup-call]] initializes the buffer local variables =org-export-before-parsing-hook=, =org-latex-classes=, =org-latex-title-command=, =org-latex-toc-command=, and =org-latex-subtitle-format=. #+caption[Call =my-ox-latex-export-local-variables=]: #+caption: Use the setup macro for =ox-latex=. #+name: lst:emacs-lisp-setup-call #+header: :var title-page=lst/title-page #+begin_src emacs-lisp :tangle no (my-ox-latex-export-buffer-local-variables title-page) #+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 :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/org-contrib/babel/languages/ob-doc-LaTeX.html#orgcc214c1][Worg: backend dependent execution]] :PROPERTIES: :CUSTOM_ID: sec:backend-dependent-execution-update :END: #+caption[Worg update: backend dependent header arguments]: #+caption: Worg update: backend dependent header arguments. #+name: lst:backend-dependent-execution-update #+begin_src org :tangle worg-backend-dependent-execution-update.org ,#+latex_header: \usepackage{tikz} ,#+header: :file (by-backend (html "tree.svg") (latex nil)) ,#+header: :results (by-backend (html "raw file") (latex "latex replace")) ,#+header: :headers '("\\usepackage{tikz}\n") ,#+begin_src latex \usetikzlibrary{trees} \begin{tikzpicture} \node [circle, draw, fill=red!20] at (0,0) {1} child { node [circle, draw, fill=blue!30] {2} child { node [circle, draw, fill=green!30] {3} } child { node [circle, draw, fill=yellow!30] {4} }}; \end{tikzpicture} ,#+end_src # Setup ,#+begin_src emacs-lisp :exports none (with-eval-after-load 'ox (setq org-babel-latex-htlatex "htlatex") (defmacro by-backend (&rest body) `(cl-case org-export-current-backend ,@body))) ,#+end_src #+end_src This section updates the outdated [[https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-LaTeX.html#orgcc214c1][Worg: backend dependent execution]] example to Emacs-27.2 and Org-9.5.2. It shows how to export [[https://en.wikipedia.org/wiki/PGF/TikZ][PGF/TikZ]] images to: 1. [[https://en.wikipedia.org/wiki/PDF][PDF]] by embedding in [[https://www.latex-project.org/][LaTeX]]. 2. [[https://en.wikipedia.org/wiki/HTML][HTML]] by passing from [[https://www.latex-project.org/][LaTeX]] by [[https://en.wikipedia.org/wiki/PDF][PDF]] and to [[https://en.wikipedia.org/wiki/Scalable_Vector_Graphics][SVG]]. Listing [[lst:backend-dependent-execution-update]] tangles to [[file:worg-backend-dependent-execution-update.org][worg-backend-dependent-execution-update.org]] as either a standalone example or an example for inclusion. Finally, inclusion of [[file:worg-backend-dependent-execution-update.org][worg-backend-dependent-execution-update.org]] produces here the figure with the nodes 1, 2, 3, and 4:\\ #+include: worg-backend-dependent-execution-update.org How to include such figures as floats remains an open question. *** [[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 (with-eval-after-load 'org-element (when (autoload 'pp-display-expression "pp") (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 * Programming :PROPERTIES: :CUSTOM_ID: sec:programming :END: ** Common Lisp programming :PROPERTIES: :CUSTOM_ID: sec:common-lisp-programming :END: Listing [[lst:configure-sly]] configures the [[info:sly#Top][Sly (info)]] Common Lisp IDE for Emacs for use with [[http://www.sbcl.org/][Steel Bank Common Lisp (sbcl)]]: 1. It configures =sly-default-lisp= and =sly-lisp-implementations= as in the ~sly~ documentation string instead of in the [[info:sly#Multiple Lisps][multiple lisps (info)]] manual. 2. It ensures the [[info:sly#Auto-SLY][automatic connection to the lisp server (info)]] when opening a Common Lisp file. 3. It configures searching documentation in the [[http://www.lispworks.com/documentation/HyperSpec/Front/][Common Lisp HyperSpec]] according to the [[info:sly#Basic customization][basic customization (info)]] manual. Finally, listing [[lst:configure-sly]] uses a technique to [[info:sly#Loading Slynk faster][load Slynk faster (info)]] by means of a custom core file in src_emacs-lisp{no-littering-var-directory}. Listing [[lst:sbcl-core-for-sly]] tangles to a script to dump such a [[http://www.sbcl.org/][SBCL]] core. #+caption[Configure =sly=]: #+caption: Configure =sly=. #+name: lst:configure-sly #+begin_src emacs-lisp (with-eval-after-load 'sly (custom-set-variables ;; Customize `sly-default-lisp' instead of `inferior-lisp-program', ;; because `sly' uses `inferior-lisp-program' only as a backwards ;; compatibility fallback option. '(sly-default-lisp 'sbcl) `(sly-lisp-implementations '((sbcl (,(executable-find "sbcl") "--core" ,(no-littering-expand-var-file-name "sbcl.core-for-sly")))))) (add-hook 'sly-mode-hook (defun on-sly-mode-hook () (unless (sly-connected-p) (save-excursion (sly))))) (cond ((eq system-type 'darwin) (setq common-lisp-hyperspec-root "file:///usr/local/share/doc/hyperspec/HyperSpec/") (setq common-lisp-hyperspec-symbol-table (concat common-lisp-hyperspec-root "Data/Map_Sym.txt"))) ((eq system-type 'gnu/linux) (setq common-lisp-hyperspec-root "file:///usr/share/doc/hyperspec-7.0/HyperSpec/")) (t (message "Default Common Lisp HyperSpec access"))) (define-key sly-prefix-map (kbd "M-h") #'sly-documentation-lookup)) #+end_src #+caption[Script to dump a SBCL core for the Sly Common Lisp IDE]: #+caption: Script to dump a SBCL core for the Sly Common Lisp IDE. #+header: :tangle-mode (identity #o755) #+name: lst:sbcl-core-for-sly #+begin_src shell :noeval :tangle ~/bin/sbcl.core-for-sly #!/bin/sh sbcl <