.githooks | ||
etc | ||
.gitignore | ||
.ignore | ||
elisp-to-cl-lesson.org | ||
Emacs-logo.png | ||
fdl-1.3.txt | ||
gpl-3.0.txt | ||
Makefile | ||
org-babel-tangle-file | ||
Org-mode-unicorn.png | ||
README.org |
Emacs setup for use with LaTeX, Lisp, Org, and Python
- Copying
- Quick start
- Introduction
- Early Init File (info)
- Init File (info) header
- Install the selected packages (info)
- Emacs Tree-sitter
- Text faces or styles (info)
- Window management (info)
- Advising functions (info)
- Bookmarks (info)
- Registers (info)
- Dired: directory editor as file manager (info)
- Minibuffer completion styles (info)
- Processes (info)
- Help (info)
- Key bindings (info)
- Using Emacs as a server (info)
- Calendar, Diary, and Time (info)
- Tools to handle buffers and modes
- Completion
- Search and replace (info)
- Version Control (info)
- Reading
- Writing
- Writing LaTeX files
- Writing Markdown files
- Writing Org (info) files and Org activation (info)
- Setup Org
- Org HTML style default
- Buffer properties
- Column View (info) and Property Syntax (info)
- Org introspection
- Citar: citing bibliography with Org Mode
- Compare bibtex and biblatex
- Engrave Faces
- Making Org hyperlink types (info)
- Extending org-mode to handle YouTube links
- Translate capital keywords (old) to lower case (new)
- Evaluate specific source blocks at load-time
- Org-mode macro utilities
- Testing Org
- LaTex preamble editing using Noweb (info)
- Deprecated LaTeX preamble editing methods
- HTML export (info)
- Advanced LaTeX export settings
- Org Syntax
- Grammar, spelling, and style tools
- Which-function-mode (info)
- Saving Emacs sessions (info)
- Programming Tools
- Programming Modes
- Libraries
- Minor Modes (info)
- Display (info)
- Applications
- Init File (info) footer
- Local variables linking to Latexmk save-compile-display-loop
,#+latex_header: <<latex-header-1>>
,#+latex_header: <<latex-header-2>>
,#+latex_header: <<latex-header-3>>
,#+latex_header: <<latex-header-4>>
,#+latex_header: <<latex-header-5>>
Copying
This README contains my Emacs setup for use with LaTeX, Lisp, Org, and Python. Copyright © 2021-2023 Gerard Vermeulen.
All Org-mode source blocks with function definitions in this document are free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
Permission is granted to copy, distribute and/or modify the other parts of 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. You should have received a copy of the GNU Free Documentation License with the source file of this document. If not, see <https://www.gnu.org/licenses/>.
Quick start
This setup requires at least Emacs-30. The installation procedure starts with:
- Backup the
user-emacs-directory
which defaults often to~/.emacs.d
. - Run the listing prepare the
user-emacs-directory
withoutssh
access or the listing prepare theuser-emacs-directory
withssh
access commands.
After invoking Emacs interactively, Emacs will ask you to install a selected set of packages. Quit Emacs and invoke Emacs again.
cd ~
git clone https://forge.chapril.org/gav451/emacs.d.git .emacs.d
make --directory=.emacs.d init
emacs &
cd ~
git clone ssh://gitea@forge.chapril.org:222/gav451/emacs.d.git .emacs.d
make --directory=.emacs.d init
emacs &
Introduction
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 Org Mode plain text with Python source code blocks and the file format of the presentations is LaTeX.
This org file (more precisely the original org source file of this file) illustrates three methods in my work-flow:
- How to tangle (or export) source blocks from org files. This file contains
source blocks to produce the files
early-init.el
,init.el
,latexmkrc
,org-store-link
, andexample.py
by tangling. - How to export org files to other formats such as HTML, LaTeX, and PDF.
- How org hyperlinks (info) allow to link inside and outside Org Mode: hover over or click on the links to experiment.
The AUCTeX - Aalborg University Center TeX extension package provides a powerful Text-based User Interface (TUI) environment to edit the LaTeX presentations.
The Citar extension package provides quick filtering and selecting of bibliographic entries, and the option to run different commands on those selections. Citar exploits the extension packages Vertico, Embark, Marginalia, and Consult. The CiteProc extension package provides CSL: citation style language processing capabilities to Citar and Org Mode. A curated repository of CSL styles is the citation style language repository.
The PDF-Tools extension package renders PDF file with the possibility to
annotate the file or to click on anchors in the PDF file that link back to the
original LaTeX file of the document. An example of my work-flow are the steps
to convert this org file to PDF and to see the result with 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
LaTeX compilation loop to update and redisplay the PDF file after execution of
the org-latex-export-latex-to-latex
command in this buffer.
Here follows a list of interesting Emacs configurations:
- Kyle Meyer's configuration shows the working environment of an Emacs and Org developer.
- Musa Al-hassy's configuration is an impressive example of producing the Emacs initialization files and other files by tangling an org file. His methodology is impressive, as his Elisp Cheat Sheet and org-special-block-extra package show. To me, this is a configuration to admire, but his methodology is way over my head.
- Omar Antolin Camarena's configuration exploits built-in packages, Omar's own
small packages, and large external packages. Omar is the author of orderless
and embark. My use
setopt
to set Emacs options instead of the customize interface comes from his idea of usingcustom-set-variables
although that idea is bad Emacs advice for new users. - 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.
- Sacha Chua's configuration is a practical example of producing the Emacs initialization files by tangling an org file. It gives me the impression that she is a practical person trying to achieve her goals by the most efficient means.
- 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 The order of company candidates is incorrect in Emacs lisp mode.
Here follows a list of links on how to use Emacs and Elisp:
- Mastering Emacs is a link to a blog with interesting posts that promotes a book on how to become a proficient Emacs user.
- 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 Mastering Emacs book.
- A guided tour of Emacs is a link to a video tour pointing how buffers (info), dired (info), documentation (info), elisp (info), elisp debugging (info), eshell (info), and keyboard macros (info) turn Emacs in an powerful coding and editing environment.
- Emergency Emacs is a link to a video on fundamental editing features and principles with focus on ediff (info), undo (info), moving point (info), erasing (info), and dynamic abbreviations (info).
- Endless Parentheses is a blog with mindblowing code snippets.
- How to open a file in Emacs looks into elisp (info) and elisp debugging (info) and then compares Emacs, Vim, Neovim, and Visual Studio Code with respect to values and technology.
- Learning Emacs and Elisp is a link to a video tutorial with a transcript on the best approach to learn Emacs and Elisp.
Early Init File (info)
Try to load no-littering as early as possible, since it helps to keep
~/.emacs.d
clean.
;;; 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
In order to get help in understanding the code block above in a buffer showing the original Org source file, type
(eval
after moving point (or cursor) to one of the items of the list:- src_emacs-lisp[:results none]{(describe-variable #'load-prefer-newer t)}
- src_emacs-lisp[:results none]{(apropos-library "no-littering")}
- 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.
Init File (info) header
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 init file (info) does load the custom-file
according to the recommendation
of saving customizations (info).
;;; init.el --- user init file -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:
(require 'cl-lib)
(when (< emacs-major-version 30)
(error "This `init.el' requires at least Emacs-30"))
(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")
;; Known problem: EasyPG fails to save files encrypted with GnuPG v2.4.1.
;; Darwin: brew install gnupg@2.2
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 ""
isearch-lazy-count t
kill-ring-max 300
lazy-count-prefix-format nil
lazy-count-suffix-format " (%s/%s)"
lazy-highlight-initial-delay 1.0
mode-line-compact t
next-error-message-highlight t
recentf-max-saved-items 100
recentf-mode t
scroll-bar-mode nil
tab-always-indent 'complete
tab-width 8
tool-bar-mode nil
tooltip-mode nil
url-cookie-trusted-urls nil
url-cookie-untrusted-urls '(".*")
use-dialog-box nil
use-short-answers t
view-read-only t)
(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 nil
;; Pin packages to GNU ELPA or to MELPA STABLE for info and/or stability.
package-pinned-packages '((auctex . "gnu")
(citar . "melpa-stable")
(citar-embark . "melpa-stable")
(compat . "gnu")
(consult . "gnu")
(dash . "melpa-stable")
(debbugs . "gnu")
(denote . "gnu")
(embark . "gnu")
(embark-consult . "gnu")
(emms . "gnu")
(engrave-faces . "gnu-devel")
(f . "melpa-stable")
(forge . "melpa-stable")
(git-commit . "nongnu")
(keycast . "nongnu")
(magit . "nongnu")
(magit-section . "nongnu")
(marginalia . "gnu")
(osm . "gnu")
(parsebib . "melpa-stable")
(queue . "gnu")
(rainbow-mode . "gnu")
(s . "melpa-stable")
(xr . "gnu")
(vertico . "gnu")
(with-editor . "nongnu")
(yasnippet . "gnu"))
package-selected-packages '(debbugs no-littering))
;; Tweak warnings:
(setopt warning-display-at-bottom nil
;; Suppress the missing lexical-binding cookie warnings.
;; warning-suppress-log-types '((lexical-binding))
)
;; To use different Org git repositories (org or gro):
(push (expand-file-name "~/VCS/org-mode/lisp") load-path)
(push (expand-file-name "~/VCS/org-contrib/lisp") load-path)
;; (byte-recompile-directory "~/VCS/org-contrib/lisp" 0)
;; Postpone (require 'org) after shadowing Org and sh-script faces below.
(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)))
(when (file-exists-p custom-file)
(load custom-file))
Install the selected packages (info)
Emacs installs packages from archives on the internet. This setup uses five archives:
- The GNU Emacs Lisp Package Archive.
- The Development Emacs Lisp Package Archive.
- The NonGNU Emacs Lisp Package Archive.
- The Milkypostman's Emacs Lisp Package Archive (MELPA) with its official mirror in case is Melpa.org down? tells MELPA is down for everyone.
- The 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 no-littering is not present:
- It installs and loads no-littering after ensuring refreshing of the contents of available packages.
- It calls src_emacs-lisp[:results silent]{(package-install-selected-packages)} to check the installation status of all packages in src_emacs-lisp[:results silent]{package-selected-packages} and to install the missing packages after the user has agreed to its prompt.
- It defines a function to ensure the installation of packages in other source blocks.
In case of normal Emacs usage, src_emacs-lisp[:results silent]{(list-packages)} refreshes the contents of packages and allows to update packages to the latest version.
(defvar package-selected-packages)
(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) ; Use builtin or git!
;; 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))
Emacs Tree-sitter
Mickey Peterson's post How To Get Started with Tree-Sitter explains how to use
tree-sitter
in Emacs-29.1. Listing Setup treesit
grammar sources configures
where to find the different tree-sitter
grammar sources for different
languages. Listing Setup Go programming shows how to configure tab-width
for
go-ts-mod
and go-mode-ts-mode
.
(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")
(lua "https://github.com/MunifTanjim/tree-sitter-lua" "main" "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"))))
Text faces or styles (info)
This setup does not use custom themes (info), but it does a minimal setup of fonts and faces. The note on mixed font heights in Emacs for how to setup fonts properly. It boils down to two rules:
- The height of the default face must be an integer number to make the height a physical quantity.
- 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 implements those rules. Listing lst:face-setting-functions shows that font scaling is easy in case of proper initialization of all face heights in a frame. To adjust the font size in windows, invoke src_emacs-lisp adjust the font size in the current buffer, type to invoke src_emacs-lisp[:results silent]{(text-scale-adjust +1)} before making further adjustments as displayed. Listing lst:face-setting-functions, lst:shadow-org-font-lock-faces, and lst:shadow-emacs-font-lock-faces show tweaks to improve visibility without theming:
- Toggling between a dark and light background by means of src_emacs-lisp{(invert-default-face)}.
- Shadowing the definition of faces before loading. The last item in the page on fontifying Org mode source code blocks describes this method.
Listing lst:use-buffer-face-mode-for-fixed-or-variable-pitch-face contains
functions to set a fixed or variable pitch face in the current buffer and
selects a fixed pitch face for magit-mode
and progmode
buffers. Type
(eval
to inspect faces at point.(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"))))
(with-eval-after-load 'emacs
;; Neither `set-default-face-height' nor `text-scale-adjust' work well.
(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 height (%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)))
(defun invert-default-face ()
"Invert the default face."
(interactive)
(invert-face 'default)))
(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 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'."
:group 'org-faces)
(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."
:group 'org-faces))
(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."
:group 'sh-indentation))
(require 'org)
;; Use proportional font faces in current buffer
(defun set-buffer-variable-pitch-face ()
"Set a variable width (proportional) font in current buffer."
(interactive)
(setq-local 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-local 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)
(when (fboundp 'visual-wrap-prefix-mode)
(add-hook 'visual-line-mode-hook 'visual-wrap-prefix-mode))
Window management (info)
Mickey Peterson's post 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. See:
- The Zen of Buffer Display (info)
- Configuring the Emacs display system
for technical information.
(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 (buf-name _action)
(with-current-buffer buf-name (apply #'derived-mode-p major-modes))))
(keymap-global-set "M-o" #'other-window))
(with-eval-after-load 'emacs
(winner-mode +1))
(with-eval-after-load 'emacs
;; https://www.masteringemacs.org/article/demystifying-emacs-window-manager
;; GAV: `display-buffer-alist' fails to set the *Warnings* buffer position.
(setopt switch-to-buffer-obey-display-actions t)
(add-to-list 'display-buffer-alist
`(,(rx (or "*Apropos*" "*Dictionary*" "*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)))
;; Handling the *Warnings* buffer like the *compilation* buffer also fails.
;; BUG#70773: Eli tells `(setq delayed-warnings-hook nil)' is better.
;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-05/msg00253.html
;; Works after setting `warnings-display-at-bottom' to `nil'.
(add-to-list 'display-buffer-alist
`(,(rx (or "*Warnings*" "*compilation*"))
display-buffer-no-window
(allow-no-window . t))))
Advising functions (info)
(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)))))
(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)))
Bookmarks (info)
Listing bookmark setup preserves bookmark history in bookmark-default.el. Table
tab:bookmark-commands-and-bindings lists all bookmark
commands with working
key bindings. Not all key bindings in src_emacs-lisp[:results
silent]{(describe-variable 'bookmark-map)} are operational for different
reasons.
BUG: The virtual buffers of the consult-buffer
command do not seem to work.
Therefore, consult-buffer
is no means to access the bookmark history (contrary
to claims in consult (info)).
(with-eval-after-load 'bookmark
(setopt bookmark-save-flag 1)
(setf bookmark-exit-hook 'bookmark-unload-function))
command | key binding |
bookmark-delete | |
bookmark-delete-all | |
bookmark-insert | (eval |
bookmark-insert-location | |
bookmark-jump | (eval |
bookmark-jump-other-frame | |
bookmark-jump-other-window | |
bookmark-load | |
bookmark-rename | |
bookmark-save | |
bookmark-set | (eval |
bookmark-set-no-overwrite | (eval |
bookmark-write | |
consult-bookmark | (eval |
list-bookmarks |
Registers (info)
Listing register separator usage example shows how to use a register
(eval
as register separator when appending regions to text registers. Listing setup register usage prepares my preferred register usage. Table tab:register-commands-and-bindings lists allregister
commands with working
key bindings. The desktop
library allows to preserve the register
history
between Emacs sessions.
When I set register ?+ to
(set-register ?+ "\n*REGISTER SEPARATOR*\n")
to test append-to-register on two regions like
Mark this line without new-line as region 1
Mark this line without new-line as region 2
The correct result of
"C-x r s T" with region 1 marked
"C-x r + T" with region 2 marked
"C-x r i T"
is:
Mark this line without new-line as region 1
*REGISTER SEPARATOR*
Mark this line without new-line as region 2
(with-eval-after-load 'register
;; Register `?+' serves as register separator when using `append-to-register':
(set-register ?+ "\n")
;; https://emacs.stackexchange.com/a/52290
(defun delete-all-registers ()
"Delete all registers."
(interactive)
(setq register-alist nil)))
command | key binding |
append-to-register | |
copy-rectangle-to-register | (eval |
copy-to-register | (eval |
frameset-to-register | (eval |
increment-register | (eval |
insert-register | (eval |
jump-to-register | (eval |
kmacro-to-register | (eval |
number-to-register | (eval |
point-to-register | (eval |
prepend-to-register | |
set-register | |
window-configuration-to-register | (eval |
Dired: directory editor as file manager (info)
Dired (info) and the ranger file manager offer similar capabilities for copying, deleting, opening, renaming, and viewing files and directories, but the integration of dired (info) in Emacs is obviously much better than the integration of ranger in Emacs.
For instance, this setup allows to insert an org-mode
link to a file
containing a specific image into an org-mode
buffer by means of the facilities
of dired (info) as follows:
- Type
(eval
to open the directory containing the file with the specific image in adired-mode
buffer. - Start searching for the specific image by navigating to the name of any image file.
- Type
(eval
to switch to view the file in animage-mode
buffer. -
In the
image-mode
buffer, continue searching for the specific image by typing:(eval
to view the next image in thedired-mode
buffer,(eval
to view the previous image in thedired-mode
buffer, and(eval
to quit theimage-mode
buffer and to go back to thedired-mode
buffer.
- After finding the specific image, use
(eval
in theimage-mode
buffer or thedired-mode
buffer to store theorg-link
. - Switch to the
org-mode
buffer and use(eval
to insert theorg-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.
(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 'wdired
(setopt wdired-allow-to-change-permissions t))
Listing lst:extra-dired-key-bindings adds a new key binding to dired-mode-map
to open files with the Emacs Web Wowser inside Emacs.
(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))))
(keymap-set dired-mode-map "E" #'dired-eww-open-file))
Minibuffer completion styles (info)
Listing set minibuffer options implements ideas of the post Understanding minibuffer completion.
(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)))
Processes (info)
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
.
;; 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)))))
Help (info)
Listing lst:setup-help enriches the help buffer. Table tab:help-key-bindings lists a number of key bindings to start playing with the help facilities of Emacs. Try .
(with-eval-after-load 'help-fns
(add-hook 'help-fns-describe-function-functions
#'shortdoc-help-fns-examples-function)
(setopt help-enable-symbol-autoload t))
command | key map | keys |
Info-goto-emacs-command-node | help-map | (eval |
describe-function | help-map | (eval |
describe-key | help-map | (eval |
describe-symbol | help-map | (eval |
describe-variable | help-map | (eval |
info | help-map | (eval |
Shortdoc-display-group (info)
Listing lst:configure-shortdoc binds
(eval
toshort-doc-display-group
and defines a short documentation group for functions
defined in this Org file.
(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-insert-source-block :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))))
Info (info)
Listing lst:configure-info adds the required paths to the places where info
looks for files.
(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 the git Org info files.
(add-to-list 'Info-directory-list
(expand-file-name "~/VCS/org-mode/doc")))
Key bindings (info)
command | key map | keys |
undo | global-map | (eval |
backward-kill-word | global-map | (eval |
backward-char | global-map | (eval |
forward-char | global-map | (eval |
backward-word | global-map | (eval |
forward-word | global-map | (eval |
backward-sentence | global-map | (eval |
forward-sentence | global-map | (eval |
Disabling Commands (info)
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 disabled commands on the fly.
(with-eval-after-load 'emacs
(setq disabled-command-function
(defun enable-this-command (&rest _args)
"Called when executing a disabled command.
Enable it and re-execute it."
(put this-command 'disabled nil)
(message "You typed %s. Emacs enabled %s."
(key-description (this-command-keys)) this-command)
(sit-for 0)
(call-interactively this-command))))
Interaction-log
(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))
Keycast
Listing lst:configure-keycast configures keycast
.
;; Make `keycast-log-update-buffer' use a buffer similar to the
;; control buffer `ediff-setup-windows-plain' returns.
(when (ensure-package-installation 'keycast)
(setopt keycast-log-newest-first t
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))
Using Emacs as a server (info)
Emacs can act as a server that listens to a socket to share its state (for
instance buffers and command history) with other programs by means of a shell
command emacsclient
. Section #sec:latexmk-save-compile-display-loop and
#sec:qutebrowser-userscript show how to use emacsclient
to:
- Install an asynchronous (or background) loop of saving a LaTeX file, compiling it, and redisplaying the output in Emacs.
- Make qutebrowser send html links with document titles to Emacs.
The code in listing lst:start-emacs-server starts the Emacs server.
(when window-system
(unless (or noninteractive (daemonp))
(add-hook 'after-init-hook #'server-start)))
Latexmk save-compile-display loop
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 local variables section
(only visible in org
files, but not in html
and pdf
files) shows how to
make use of the latexmkrc
file.
# 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:
Qutebrowser userscript
The next block contains an userscript that sends a store-link org-protocol
message with the url and the title from qutebrowser to emacsclient
. The
function urlencode
translates the url and the title for the message. The
Python urllib examples show how to use urlencode
. The final execvp
call
deals with a 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 qutebrowser.
On a POSIX system, you can run the userscript from qutebrowser or from a terminal to see whether it works. In case you try to run it from Emacs, Emacs may hang or die.
#!/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))
Calendar, Diary, and Time (info)
(with-eval-after-load 'time
(setopt world-clock-list
'(("America/Los_Angeles" "Seattle")
("America/New_York" "New York")
("Europe/London" "London")
("Europe/Moscow" "Moscow")
("Europe/Paris" "Paris")
("Europe/Istanbul" "Istanbul")
("Africa/Cairo" "Cairo")
("Asia/Jerusalem" "Jerusalem")
("Asia/Singapore" "Singapore")
("Asia/Tokyo" "Tokyo"))
world-clock-time-format "%a %d %b %R %z"))
Tools to handle buffers and modes
(defun set-all-buffers-with-modes (minor-mode value modes)
"Set MINOR-MODE to VALUE for all buffers with a mode derived from MODES."
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (and (derived-mode-p modes) (buffer-file-name))
(funcall minor-mode value)))))
(defun enable-ro-all-org-mode-buffers ()
"Enable `buffer-read-only' of all `org-mode' buffers."
(interactive)
(set-all-buffers-with-modes #'read-only-mode +1 '(org-mode))
(message "Enabled `buffer-read-only' of all `org-mode' buffers."))
(defun disable-ro-all-org-mode-buffers ()
"Disable `buffer-read-only' of all `org-mode' buffers."
(interactive)
(set-all-buffers-with-modes #'read-only-mode -1 '(org-mode))
(message "Disabled `buffer-read-only' of all `org-mode' buffers."))
(defun active-major-modes ()
"Return a list of active major modes found by iterating over all buffers."
(let (result)
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(let ((mode major-mode))
(push mode result))))
(cl-sort (cl-remove-duplicates result) #'string-lessp)))
Completion
Vertico (info) provides a performant and minimalistic vertical completion UI based on the default completion system and behaves therefore correctly under all circumstances. Using Vertico, Marginalia, Consult, and Embark links to a video demonstration. Vertico integrates well with fully supported complementary packages to enrich the completion UI:
- Embark (info) for minibuffer actions with context menus,
- Marginalia (info) for rich annotations in the minibuffer, and
- Consult (info) for useful search and navigation commands,
where the order is that of enhancing citar's experience and the configuration steps below.
Finally, company: a modular complete-anything framework for Emacs provides completion in any buffer.
Vertico (info)
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. BUG: Adding eww-history
, register-alist
, regexp-search-string
, or
search-string
to savehist-additional-variables
fails to save the relevant
histories.
(when (require 'savehist nil 'noerror)
(setopt savehist-additional-variables '(kill-ring search-ring))
(savehist-mode +1))
(when (and (ensure-package-installation 'vertico)
(fboundp 'vertico-directory-delete-char)
(fboundp 'vertico-directory-delete-word)
(fboundp 'vertico-mode)
(fboundp 'vertico-directory-enter))
(vertico-mode +1)
(with-eval-after-load 'vertico
(defvar vertico-map)
(keymap-set vertico-map "DEL" #'vertico-directory-delete-char)
(keymap-set vertico-map "M-DEL" #'vertico-directory-delete-word)
(keymap-set vertico-map "RET" #'vertico-directory-enter)))
command | remap | keys |
vertico-directory-delete-char | (eval |
|
vertico-directory-delete-word | (eval |
|
vertico-directory-enter | (eval |
|
vertico-exit | exit-minibuffer | (eval |
vertico-exit-input | (eval |
|
vertico-first | beginning-of-buffer | (eval |
vertico-first | minibuffer-beginning-of-buffer | (eval |
vertico-insert | (eval |
|
vertico-last | end-of-buffer | (eval |
vertico-next-group | forward-paragraph | (eval |
vertico-next | next-line-or-history-element | (eval |
vertico-next | next-line | (eval |
vertico-previous-group | backward-paragraph | (eval |
vertico-previous | previous-line-or-history-element | (eval |
vertico-previous | previous-line | (eval |
vertico-save | kill-ring-save | (eval |
vertico-scroll-down | scroll-down-command | (eval |
vertico-scroll-up | scroll-up-command | (eval |
Embark (info)
Listing bind embark
commands binds embark
commands to the global key map:
embark-act
prompts the user for an action and performs it. GAV:org-mode
andembark
do not play together nicely.-
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. Sinceembark-dwim
acts on a superset of target types, it rendersgoto-addr
superfluous. See:- Goto Address mode (info).
- The default (embark-dwim) action on a target (info).
embark-bindings
allows to explore all current command key bindings in the minibuffer.- The initialization of
prefix-help-command
enables minibuffer help after a prefix key (for instance ) as typing shows.
;; GAV: ~org-mode~ and ~embark~ do not play together nicely.
;; GAV: ~embark~ is a dependency of ~citar-embark~ and ~embark-consult~.
(when (ensure-package-installation '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)))
Marginalia (info)
Listing lst:enable-marginalia-mode enables marginalia-mode
.
(when (and (ensure-package-installation 'marginalia)
(fboundp 'marginalia-mode))
(marginalia-mode +1))
Consult (info)
Consult (info) provides practical commands based on the Emacs minibuffer
completion function completing-read. Listing bind consult
commands binds
consult
commands to different key maps.
Consult usage tips are:
- Add a buffer position to the mark ring by means of
(eval
and jump to one of the marks in the ring by means of(eval
lt-mark)}}} or(eval
. See Emacs: Mark Ring. - is an alternative to .
- Listing lst:find-file-preview uses the internal functions
consult--file-preview
andconsult--read
to visit files with previewing during selection. See: Consult Wiki.
command | key map | keys |
consult-bookmark | ctl-x-r-keymap | (eval |
consult-buffer-other-frame | ctl-x-5-keymap | (eval |
consult-buffer-other-window | ctl-x-4-keymap | (eval |
consult-buffer | ctl-x-keymap | (eval |
consult-compile-error | goto-map | (eval |
consult-complex-command | ctl-x-keymap | (eval |
consult-find | search-map | (eval |
consult-focus-lines | search-map | (eval |
consult-git-grep | search-map | (eval |
consult-global-mark | goto-map | (eval |
consult-goto-line | goto-map | (eval |
consult-goto-line | goto-map | (eval |
consult-history | global-map | (eval |
consult-imenu | goto-map | (eval |
consult-keep-lines | search-map | (eval |
consult-line | search-map | (eval |
consult-mark | goto-map | (eval |
consult-mode-command | global-map | (eval |
consult-multi-occur | search-map | (eval |
consult-org-heading | org-mode-map | (eval |
consult-outline | goto-map | (eval |
consult-register | ctl-x-r-keymap | (eval |
consult-yank-pop | global-map | (eval |
elfeed | global-map | (eval |
embark-act | global-map | (eval |
embark-bindings | global-map | (eval |
embark-dwim | global-map | (eval |
iedit-mode | global-map | (eval |
minibuffer-complete-history | minibuffer-local-map | (eval |
narrow-or-widen-dwim | ctl-x-keymap | (eval |
org-agenda | global-map | (eval |
org-capture | global-map | (eval |
org-cite | org-mode-map | (eval |
org-goto | org-goto | (eval |
org-insert-link-global | global-map | (eval |
org-narrow-to-table | ctl-x-keymap | (eval |
org-store-link | global-map | (eval |
(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)
(with-eval-after-load 'org
(keymap-set org-mode-map "C-c C-h" #'consult-org-heading))
;; 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))
(defun consult-find-file-preview
(prompt &optional dir _default mustmatch initial predicate)
"Helper to implement previewing when calling `find-file'.
See `read-file-name' for the meaning of PROMPT, DIR, MUSTMATCH, INITIAL,
and PREDICATE."
(interactive)
(let ((default-directory (or dir default-directory))
(minibuffer-completing-file-name t))
(consult--read #'read-file-name-internal :state (consult--file-preview)
:prompt prompt :initial initial
:require-match mustmatch :predicate predicate)))
(defun toggle-find-file-preview ()
"Toggle previewing when calling `find-file'."
(interactive)
(let ((done "Enabled"))
(if (eq read-file-name-function #'read-file-name-default)
(setq read-file-name-function #'consult-find-file-preview)
(setq read-file-name-function #'read-file-name-default
done "Disabled"))
(message "%s `find-file' previewing" done)))
Company (info)
Company (info) is a modular completion framework and listing lst:setup-company
configures company
after ensuring the company
installation.
(when (and (ensure-package-installation 'company)
(fboundp 'company-mode))
;; https://github.com/purcell/emacs.d/issues/778
(setopt company-transformers '(company-sort-by-occurrence))
(dolist (symbol '(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 symbol #'company-mode)))
Search and replace (info)
Regexp Replace (info)
Executing
(eval
prompts for a regular expressionREGEXP
and a substitution NEWSTRING
to replace REGEXP
with NEWSTRING
.
The tools in section Narrowing allow to limit the scope of {{{kbd(M-x
replace-regexp)}}}. A list pointing to relevant documentation is:
- Regexp Replace (info) explains how the substitution handles references to regular expression groups and lisp expressions.
- Syntax of Regular Expressions (info) describes regular expression features for users.
- Emacs Lisp Regular Expressions (info) describes regular expression features for programmers.
- Emacs Wiki has an article on replacing regexp with lisp expressions.
Table tab:replace-regexp-tranform tabulates how to perform example tasks by means of
(eval
using(regular-expression transform)
pairs.
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+ \#)._ |
Incremental Search (info) and Other Search and Loop Commands (info)
;; https://www.masteringemacs.org/article/searching-buffers-occur-mode
(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))))
;; https://blog.chmouel.com/posts/emacs-isearch/
(when (package-installed-p 'consult)
(defun consult-line-from-isearch ()
"Invoke `consult-line' from isearch."
(interactive)
(let ((query (if isearch-regexp
isearch-string
(regexp-quote isearch-string))))
(isearch-update-ring isearch-string isearch-regexp)
(let (search-nonincremental-instead)
(ignore-errors (isearch-done t t)))
(consult-line query)))
(keymap-set isearch-mode-map "C-l" #'consult-line-from-isearch))
;; https://blog.chmouel.com/posts/emacs-isearch/
(defun occur-from-isearch ()
"Invoke `occur' from isearch."
(interactive)
(let ((query (if isearch-regexp
isearch-string
(regexp-quote isearch-string))))
(isearch-update-ring isearch-string isearch-regexp)
(let (search-nonincremental-instead)
(ignore-errors (isearch-done t t)))
(occur query)))
(defun project-find-regexp-from-isearch ()
"Invoke `project-find-regexp' from isearch."
(interactive)
(let ((query (if isearch-regexp
isearch-string
(regexp-quote isearch-string))))
(isearch-update-ring isearch-string isearch-regexp)
(let (search-nonincremental-instead)
(ignore-errors (isearch-done t t)))
(project-find-regexp query)))
(keymap-set isearch-mode-map "C-o" #'occur-from-isearch)
(keymap-set isearch-mode-map "C-f" #'project-find-regexp-from-isearch)
Ugrep
Ugrep is an ultra fast grep with interactive query UI and fuzzy search. The
pages Using ugrep within Emacs and Ugrep in Emacs show how to make Emacs use it
with lgrep and xref. Listing set grep
and xref
options shows my
implementation of the suggestions on those pages. To do:
- None of the commands listed in Searching with Grep under Emacs (info)
works on my system with the exception of
lgrep
when using ugrep. - None of the
grep
,agrep
,egrep
,fgrep
, andglimpse
builtins listed in Eshell builtins (info) works.
(when (executable-find "ugrep")
(with-eval-after-load 'grep
(setopt grep-template (string-join '("ugrep"
"--color=always"
"--decompress"
"--ignore-binary"
"--ignore-case"
"--include=\"<F>\""
"--line-number"
"--null"
"--recursive"
"--regexp=<R>")
" ")
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 <C> --null -snHE -e <R>")
(ripgrep . ,(concat "xargs -0 rg <C> --null -nH"
" --no-heading --no-messages -g '!*/' -e <R>"))
(ugrep . "xargs -0 ugrep <C> --null -ns -e <R>")))))
XR - Emacs regexp parser and analyzer
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.
(when (ensure-package-installation 'xr) t)
Version Control (info)
Ediff (info)
Video links to complete the ediff (info) manual are:
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.
(with-eval-after-load 'ediff-wind
(when (fboundp 'ediff-setup-windows-plain)
(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
(add-hook 'org-mode-hook
(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))))
Git
Magit (info)
Magit (info) is an Emacs interface to the version control system Git and the snippet below ensures its installation. Magit usage tips are:
- Most upvoted Magit questions on Stack Overflow.
- Abort an active rebase by pressing
(eval
.
I still have to understand 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)}.
(ensure-package-installation 'magit)
Github quick setup
echo "# oglot" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:gav451/oglot.git
git push -u origin main
git remote add origin git@github.com:gav451/oglot.git
git branch -M main
git push -u origin main
Learn git
Useful Git links are:
- Abort a merge in Git
- Change the URI for a Git remote
- Check out a remote branch in Git
- Clone a Git repository to a specific folder
- Clone a specific Git repository branch
- Create a new Git branch from an existing branch
- Create a remote branch in Git
- Delete a commit from a branch in Git
- Delete a file from a Git repository
- Delete a Git branch locally and remotely
- Determine the origin of a cloned Git repository
- Undo the most recent local Git commits
List all Git branches
git -C ~/VCS/oglot branch -a
* main remotes/origin/main
Reading
Reading DjVu files
Document View (info) allows to read DjVu files on condition that it can use the
command line DjVu decoder ddjvu
from the DjVuLibre utilities as shown by the
code of src_emacs-lispvu->tiff-converter-ddjvu)}.
Use to enlarge or shrink what doc-view-mode
displays and use to scroll down or up through
when pages are larger than the doc-view-mode
window.
Reading EPUB files
The package nov.el provides a major mode for reading EPUB files in Emacs. Listing lst:configure-nov configures nov.el.
(when (and (ensure-package-installation 'nov)
(fboundp 'nov-mode))
(add-to-list 'auto-mode-alist `(,(rx ".epub" eos) . nov-mode)))
Reading PDF files
The pdf-tools package exploits the poppler library to render and to let you annotate PDF files. It also exploits the SyncTeX library to link anchors in PDF files produced with LaTeX to the original LaTeX sources. In order to use pdf-tools, you have to type
(eval
after installation of pdf-tools from MELPA or after each update of poppler to build or rebuild theepdfinfo
executable that serves the PDF files to Emacs. Table
tab:pdf-view-mode-commands-and-bindings lists important local map key bindings
in pdf-view-mode
. Note: image-save
saves the page under point to a png
file.
(when (and (ensure-package-installation 'pdf-tools)
(fboundp 'pdf-loader-install))
;; Use `pdf-loader-install' for faster startup than with `pdf-tools-install'.
(pdf-loader-install)
(with-eval-after-load 'pdf-view
(setopt pdf-view-display-size 'fit-page
pdf-view-use-scaling (eq system-type 'darwin))))
command | key binding |
image-save | (eval |
pdf-outline | (eval |
pdf-view-fit-height-to-window | (eval |
pdf-view-fit-width-to-window | (eval |
Writing
Writing LaTeX files
Loading tex.el
immediately instead of lazily ensures proper initialization of
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 AUCTeX
configuration objectives:
- Listing lst:set-tex-options ensures installation of AUCTeX and initializes
AUCTeX properly for
LuaTeX
orLuaLaTeX
. - Listing lst:set-tex-options also enables indenting between square brackets which is a recent feature.
- Listing lst:update-lualatex-opentype-font-name-database defines a function to
update the
OpenType Font
name database forLuaLaTeX
when the output ofLuaLaTeX
shows missing fonts. - Listing lst:set-bibtex-options configures the Emacs
bibtex
library to use the LaTeXBiBTeX
dialect for backwards compatibility. - Listing lst:set-font-latex-options disables font scaling of section titles.
- Listing lst:set-latex-options configures
latex
for a full featuredLaTeX-section-command
.
(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-parse-self t
;; Disable `TeX-electric-math' to prevent collisions with `smartparens'.
TeX-electric-math nil)))
(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))))))
(with-eval-after-load 'bibtex
(setopt bibtex-dialect 'BibTeX))
(with-eval-after-load 'font-latex
(setopt font-latex-fontify-sectioning 1.0))
(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)))
Writing Markdown files
(when (ensure-package-installation 'markdown-mode) t)
Writing Org (info) files and Org activation (info)
Listing lst:bind-org-commands activates Org (info) by means of global key bindings.
(with-eval-after-load 'emacs
(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))
Links to Org-mode videos are:
- Analyze Your Time with Org Mode Clocktables
- Basic Task Management with Org: Checklists, TODOs, and Org-Agenda
- Emacs Org-mode: a system for note-taking and project planning
- Getting Started With Org Mode
- Org Mode Time and Task Tools
Setup Org
I have split the initial Org-mode setup over fourteen listings. Here, follows a list detailing and motivating each listing:
- Listing lst:set-org-options handles basic Org-mode options.
- Listing lst:org-capture-templates initializes a simple capture template.
- Listing lst:setup-org-babel sets
ob-core
,ob-latex
, andob-lisp
options. See working with source code (info) for Org Babel information. - Listing lst:no-org-babel-eval prevents evaluation of blocks without an
org-babel-execute:<LANG>
function and adds fakeorg-babel-execute:<LANG>
functions to silence(eval
warnings. - Listing lst:set-org-link-options handles Org-mode options specific to hyperlinks (info).
- Listing lst:looking-at-org-id enables recent
org-id
features. - Listing lst:setup-org-mode-map-1 and lst:setup-org-mode-map-2 extend the
org-mode-map
with useful key-bindings. - Listing lst:setup-org-src facilitates editing source code blocks, and it
provides functions to change the indentation of all
org-src-mode
blocks. - Listing lst:setup-ob-python allows to pretty-print Python session source
block values with black instead of pprint. This snippet may break in the
future, because it sets
org-babel-python--def-format-value
which is a constant declared "private" by two dashes in its name! - Listing lst:set-org-export-options selects the
non-intrusive
expert user interface for export dispatching. - Listing lst:setup-org-for-lualatex-export and lst:set-ox-latex-options-for-lualatex-export configure Org-mode to generate output for the LuaLaTeX compiler.
- Listing lst:setup-org-latex-classes defines org-latex-classes (info) for
backward compatibility. For an explanation of the code in listing
lst:setup-org-latex-classes, type
(eval
.
(with-eval-after-load 'org
(setopt
org-agenda-files '("gerard.org")
org-agenda-include-diary t
org-babel-load-languages `((calc . t)
(dot . ,(fboundp 'grapviz-dot-mode))
(emacs-lisp . t)
(eshell . t)
(fortran . t)
(gnuplot . ,(fboundp 'gnuplot-mode))
(julia . t)
(latex . t)
(lilypond . ,(fboundp 'lilypond-mode))
(lisp . t)
(lua . ,(fboundp 'lua-mode))
(org . t)
(perl . t)
;; Beware of circular Python dependencies.
(python . t)
(ruby . ,(fboundp 'ruby-mode))
(shell . t))
org-directory "~/org"
org-export-backends '(ascii beamer html icalendar latex md 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-collector
org-id
org-protocol
org-tempo)
org-use-property-inheritance t))
(with-eval-after-load 'org-capture
(setopt org-capture-templates
'(("f" "Flat capture database" entry (file "flat.org")
"* %^{Prompt}t %?
%a"))))
(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")))
(with-eval-after-load 'ob-lisp
;; Default to `sly-eval' whenever feasible:
(when (and (package-installed-p 'sly)
(fboundp 'sly-eval))
(setopt org-babel-lisp-eval-fn #'sly-eval)))
(defvar org-babel-default-header-args:conf '((:eval . "no")))
(defvar org-babel-default-header-args:diff '((:eval . "no")))
(defvar org-babel-default-header-args:text '((:eval . "no")))
(defvar org-babel-default-header-args:toml '((:eval . "no")))
;;; Kludges to silence `org-lint' warnings:
(defun org-babel-execute:conf (_body _params)
"Silence `org-lint' warnings." nil)
(defun org-babel-execute:diff (_body _params)
"Silence `org-lint' warnings." nil)
(defun org-babel-execute:text (_body _params)
"Silence `org-lint' warnings." nil)
(defun org-babel-execute:toml (_body _params)
"Silence `org-lint' warnings." nil)
(with-eval-after-load 'ol
;; Setting `org-link-descriptive' to `t' has caused worse `isearch'
;; behavior. Set `org-link-descriptive' interactively by calling
;; `org-toggle-link-display'.
(setopt org-link-descriptive t
org-link-file-path-type 'adaptive
org-link-search-must-match-exact-headline nil))
(with-eval-after-load 'org-id
(setopt org-id-link-to-org-use-id t
org-id-track-globally t)
;; https://stackoverflow.com/a/16247032 answers
;; "How to assign IDs to all entries in a buffer."
(defun org-id-add-ids-to-headlines-in-buffer ()
"Add slowly ID properties to all headlines without such an ID property.
Undo this by means of `org-delete-property-globally'."
(interactive)
(org-map-entries #'org-id-get-create)))
;; From: "Nicolas Richard" <theonewiththeevillook@yahoo.fr>
;; 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 <SPC>" #'org-inactive-current-time-stamp)
(keymap-set org-mode-map "C-c C-<SPC>" #'org-active-current-time-stamp)
(keymap-set org-mode-map "$" #'org-electric-dollar)
(keymap-set org-mode-map "M-q" #'org-fill-paragraph))
;; Stolen from `org-insert-structure-template'.
;; Note: `org-tempo' does not require `tempo' at all.
(defcustom org-insert-source-block-defaults
'("calc -n :results silent"
"emacs-lisp -n :results silent"
"julia -n :results silent"
"latex -n"
"lisp -n :results silent :package :cs325-user"
"org -n"
"python -i -n :results silent")
"Default values for `org-insert-source-block'."
:group 'org
:type '(repeat string))
(defun org-insert-source-block ()
"Insert a source block #+begin_src/SPEC/BODY/#+end_src.
Prompt for the source block SPEC. With an active region, the
region becomes the block BODY. Otherwise, insert an empty block."
(interactive)
(let* ((spec (completing-read
"Block spec: " org-insert-source-block-defaults nil 'confirm))
(region? (use-region-p))
(region-start (and region? (region-beginning)))
(region-end (and region? (copy-marker (region-end)))))
(when region? (goto-char region-start))
(let ((column (current-indentation)))
(if (save-excursion (skip-chars-backward " \t") (bolp))
(forward-line 0)
(insert "\n"))
(save-excursion
(indent-to column)
(insert (format "#+begin_src %s\n" spec))
(when region?
(org-escape-code-in-region (point) region-end)
(goto-char region-end)
(skip-chars-backward " \n\t\r")
(end-of-line))
(unless (bolp) (insert "\n"))
(indent-to column)
(insert "#+end_src")
(if (looking-at "[ \t]*$") (replace-match "") (insert "\n"))
(when (and (eobp) (not (bolp))) (insert "\n")))
(end-of-line))))
(with-eval-after-load 'org
(keymap-set org-mode-map "C-c C-;" #'org-insert-source-block))
(with-eval-after-load 'emacs
(with-eval-after-load 'org-src
;; https://list.orgmode.org/c3cc4ff1fcfb3bc741df89a3f45be30e@posteo.net/
(setopt org-src-preserve-indentation nil
org-edit-src-content-indentation 0)
(defun org-left-shift-block ()
"Left-shift the block at point using `org-indent-block'."
(interactive)
(let ((org-adapt-indentation nil)
(org-edit-src-content-indentation 0)
(org-src-preserve-indentation nil))
(org-indent-block))))
(defun org-right-shift-block (&optional arg)
"Right-shift the block at point 4 spaces.
With prefix argument ARG, prompt for the number of spaces."
(interactive "P")
(let ((spaces 4)
(start (org-babel-where-is-src-block-head)))
(if (not start) (message "Not at block")
(when arg
(setq spaces (read-number "Number of spaces: ")))
;; The `SUBEXP' of the block body is 5.
(goto-char (match-end 5))
(forward-line -1)
(string-insert-rectangle
(match-beginning 5) (point) (make-string spaces ?\s)))))
(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))))))))
(with-eval-after-load 'ob-python
(setq org-babel-python--def-format-value "\
def __org_babel_python_format_value(result, result_file, result_params):
with open(result_file, 'w') as f:
if 'graphics' in result_params:
result.savefig(result_file)
elif 'pp' in result_params:
import black
f.write(black.format_str(repr(result), mode=black.Mode()))
elif 'list' in result_params and isinstance(result, dict):
f.write(str(['{} :: {}'.format(k, v) for k, v in result.items()]))
else:
if not set(result_params).intersection(\
['scalar', 'verbatim', 'raw']):
def dict2table(res):
if isinstance(res, dict):
return [(k, dict2table(v)) for k, v in res.items()]
elif isinstance(res, list) or isinstance(res, tuple):
return [dict2table(x) for x in res]
else:
return res
if 'table' in result_params:
result = dict2table(result)
try:
import pandas
except ImportError:
pass
else:
if isinstance(result, pandas.DataFrame) and 'table' in result_params:
result = [[result.index.name or ''] + list(result.columns)] + \
[None] + [[i] + list(row) for i, row in result.iterrows()]
elif isinstance(result, pandas.Series) and 'table' in result_params:
result = list(result.items())
try:
import numpy
except ImportError:
pass
else:
if isinstance(result, numpy.ndarray):
if 'table' in result_params:
result = result.tolist()
else:
result = repr(result)
f.write(str(result))"))
(with-eval-after-load 'ox
(setopt org-export-dispatch-use-expert-ui t))
(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))))
(when (require 'ox-latex nil 'noerror) ;; To use the safe file local variables!
(setopt
org-latex-compiler "lualatex"
org-latex-compiler-file-string
"%% -*- LaTeX -*-\n%% Intended LaTeX compiler: %s\n"
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-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)
(put 'org-latex-toc-command 'safe-local-variable 'stringp))
(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}")))))))
Org HTML style default
Set org-html-style-default
option to clean up the selection of languages and
to add HTML+CSS+JS
for mhtml-mode
:
(setopt org-html-style-default "<style>
#content { max-width: 60em; margin: auto; }
.title { text-align: center;
margin-bottom: .2em; }
.subtitle { text-align: center;
font-size: medium;
font-weight: bold;
margin-top:0; }
.todo { font-family: monospace; color: red; }
.done { font-family: monospace; color: green; }
.priority { font-family: monospace; color: orange; }
.tag { background-color: #eee; font-family: monospace;
padding: 2px; font-size: 80%; font-weight: normal; }
.timestamp { color: #bebebe; }
.timestamp-kwd { color: #5f9ea0; }
.org-right { margin-left: auto; margin-right: 0px; text-align: right; }
.org-left { margin-left: 0px; margin-right: auto; text-align: left; }
.org-center { margin-left: auto; margin-right: auto; text-align: center; }
.underline { text-decoration: underline; }
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
p.verse { margin-left: 3%; }
pre {
border: 1px solid #e6e6e6;
border-radius: 3px;
background-color: #f2f2f2;
padding: 8pt;
font-family: monospace;
overflow: auto;
margin: 1.2em;
}
pre.src {
position: relative;
overflow: auto;
}
pre.src:before {
display: none;
position: absolute;
top: -8px;
right: 12px;
padding: 3px;
color: #555;
background-color: #f2f2f299;
}
pre.src:hover:before { display: inline; margin-top: 14px;}
/* Language selection start. */
pre.src-asm:before { content: 'Assembler'; }
pre.src-asymptote:before { content: 'Asymptote'; }
pre.src-awk:before { content: 'Awk'; }
pre.src-C:before { content: 'C'; }
/* pre.src-C++ doesn't work in CSS */
pre.src-calc:before { content: 'Emacs Calc'; }
pre.src-clojure:before { content: 'Clojure'; }
pre.src-conf:before { content: 'Configuration File'; }
pre.src-cpp:before { content: 'C++'; }
pre.src-css:before { content: 'CSS'; }
pre.src-D:before { content: 'D'; }
pre.src-delphi:before { content: 'Delphi'; }
pre.src-ditaa:before { content: 'ditaa'; }
pre.src-dot:before { content: 'Graphviz'; }
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
pre.src-fortran:before { content: 'Fortran'; }
pre.src-gnuplot:before { content: 'gnuplot'; }
pre.src-haskell:before { content: 'Haskell'; }
pre.src-html:before { content: 'HTML'; }
pre.src-idl:before { content: 'IDL'; }
pre.src-J:before { content: 'J'; }
pre.src-java:before { content: 'Java'; }
pre.src-js:before { content: 'Javascript'; }
pre.src-latex:before { content: 'LaTeX'; }
pre.src-ledger:before { content: 'Ledger'; }
pre.src-lisp:before { content: 'Lisp'; }
pre.src-lilypond:before { content: 'Lilypond'; }
pre.src-lua:before { content: 'Lua'; }
pre.src-makefile:before { content: 'Makefile'; }
pre.src-matlab:before { content: 'MATLAB'; }
pre.src-maxima:before { content: 'Maxima'; }
pre.src-mercury:before { content: 'Mercury'; }
pre.src-metapost:before { content: 'MetaPost'; }
pre.src-mhtml:before { content: 'HTML+CSS+JS'; }
pre.src-modula-2:before { content: 'Modula-2'; }
pre.src-nxml:before { content: 'XML'; }
pre.src-ocaml:before { content: 'Objective Caml'; }
pre.src-octave:before { content: 'Octave'; }
pre.src-org:before { content: 'Org mode'; }
pre.src-pascal:before { content: 'Pascal'; }
pre.src-perl:before { content: 'Perl'; }
pre.src-plain-tex:before { content: 'Plain TeX'; }
pre.src-plantuml:before { content: 'Plantuml'; }
pre.src-processing:before { content: 'Processing.js'; }
pre.src-prolog:before { content: 'Prolog'; }
pre.src-ps:before { content: 'PostScript'; }
pre.src-python:before { content: 'Python'; }
pre.src-R:before { content: 'R'; }
pre.src-ruby:before { content: 'Ruby'; }
pre.src-sass:before { content: 'Sass'; }
pre.src-scala:before { content: 'Scala'; }
pre.src-scheme:before { content: 'Scheme'; }
pre.src-screen:before { content: 'Gnu Screen'; }
pre.src-sed:before { content: 'Sed'; }
pre.src-sh:before { content: 'shell'; }
pre.src-simula:before { content: 'Simula'; }
pre.src-sql:before { content: 'SQL'; }
pre.src-sqlite:before { content: 'SQLite'; }
pre.src-tcl:before { content: 'tcl'; }
pre.src-tex:before { content: 'TeX'; }
pre.src-verilog:before { content: 'Verilog'; }
pre.src-vhdl:before { content: 'VHDL'; }
pre.src-xml:before { content: 'XML'; }
/* ob-shell and sub-shells. */
pre.src-shell:before { content: 'Shell Script'; }
pre.src-bash:before { content: 'bash'; }
pre.src-csh:before { content: 'csh'; }
pre.src-ash:before { content: 'ash'; }
pre.src-dash:before { content: 'dash'; }
pre.src-ksh:before { content: 'ksh'; }
pre.src-mksh:before { content: 'mksh'; }
pre.src-posh:before { content: 'posh'; }
pre.src-zsh:before { content: 'zsh'; }
/* Language selection end. */
table { border-collapse:collapse; }
caption.t-above { caption-side: top; }
caption.t-bottom { caption-side: bottom; }
td, th { vertical-align:top; }
th.org-right { text-align: center; }
th.org-left { text-align: center; }
th.org-center { text-align: center; }
td.org-right { text-align: right; }
td.org-left { text-align: left; }
td.org-center { text-align: center; }
dt { font-weight: bold; }
.footpara { display: inline; }
.footdef { margin-bottom: 1em; }
.figure { padding: 1em; }
.figure p { text-align: center; }
.equation-container {
display: table;
text-align: center;
width: 100%;
}
.equation {
vertical-align: middle;
}
.equation-label {
display: table-cell;
text-align: right;
vertical-align: middle;
}
.inlinetask {
padding: 10px;
border: 2px solid gray;
margin: 10px;
background: #ffffcc;
}
#org-div-home-and-up
{ text-align: right; font-size: 70%; white-space: nowrap; }
textarea { overflow-x: auto; }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00; }
.org-info-js_info-navigation { border-style: none; }
#org-info-js_console-label
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
.org-info-js_search-highlight
{ background-color: #ffff00; color: #000000; font-weight: bold; }
.org-svg { }
</style>"
)
Buffer properties
(with-eval-after-load 'org
(defun org-get-buffer-properties ()
"Get all properties in buffer."
(org-element-map (org-element-parse-buffer) 'keyword
(lambda (el)
(and ;; Use (upcase "property"), else it fails.
(string= (org-element-property :key el) "PROPERTY")
(let* ((strings (split-string (org-element-property :value el)))
(value (string-join (cdr strings) " "))
(name (car strings)))
(cons name value)))))))
Column View (info) and Property Syntax (info)
Listing column view demonstration tangles to column-view-demonstration.org which complements the Org view tutorial and the Org view screencast. Listing property syntax demonstration tangles to property-syntax-demonstration.org which complements property syntax (info).
,#+TITLE: Column View Demonstration
,* Column view use
1. Type "M-x org-columns" in the "Columns view top" tree to turn on column view.
2. Press "tab" to (un-)fold the "Example 1", "Example 2", and "Example 3" rows.
3. Move point to the "OK?" cell of the "Example 1" row and type "e" to edit it.
- This changes the "Columns view top" row "OK?" field value.
4. Move point to the "DURATION" cell and type "e"" to edit it.
- This changes the "Columns view top" row "DURATION" field value.
5. Type "q" on one of the column view rows to turn off column view.
,* Column view top
SCHEDULED: <2023-06-30 Fri>
:PROPERTIES:
:COLUMNS: %17ITEM %OK(OK?){X} %OWNER %STATUS %DURATION{:} %TODO %16SCHEDULED
:OWNER_ALL: Ada Bob Carl
:STATUS_ALL: "Shopping" "Cooking" "Eating"
:OK_ALL: "[ ]" "[X]"
:END:
,** DONE Example 1
:PROPERTIES:
:OWNER: Ada
:STATUS: Shopping
:DURATION: 1:00
:OK: [X]
:END:
,** DONE Example 2
:PROPERTIES:
:OWNER: Bob
:STATUS: Cooking
:DURATION: 1:00
:OK: [X]
:END:
,** TODO Example 3
:PROPERTIES:
:OWNER: Carl
:STATUS: Eating
:DURATION: 0:00
:OK: [ ]
:END:
,#+TITLE: Property Syntax Demonstration
,* Column view use
1. Type "M-x org-columns" in the "CD collection" tree to turn on column view.
- The column view appends the value of ":GENRES+:" to that of ":GENRES:".
2. Type "q" on one of the column view rows to turn off column view.
,* Dynamic block use
1. Type "M-x org-insert-columns-dblock" to capture a column view table.
- Type headline title of the tree to capture
- The block appends the value of ":GENRES+:" to that of ":GENRES:".
,* CD collection
:PROPERTIES:
:NDISK_ALL: 1 2 3 4
:PUBLISHER_ALL: "Deutsche Grammophon" Philips EMI
:COLUMNS: %TITLE %ARTIST %GENRES %NDISK(N) %PUBLISHER
:ID: CD collection
:END:
,** Classic
:PROPERTIES:
:GENRES: Classic
:END:
,*** Goldberg Variations
:PROPERTIES:
:TITLE: Goldberg Variations
:COMPOSER: J.S. Bach
:ARTIST: G. Gould
:GENRES+: Baroque
:NDISK: 1
:PUBLISHER: Deutsche Grammophon
:END:
,#+NAME: CD collection
,#+BEGIN: columnview :hlines 1 :id "CD collection"
| TITLE | ARTIST | GENRES | N | PUBLISHER |
|---------------------+----------+-----------------+---+---------------------|
| | | | | |
| | | Classic | | |
| Goldberg Variations | G. Gould | Classic Baroque | 1 | Deutsche Grammophon |
,#+END:
Org introspection
- Listing lst:valid-org-babel-load-languages-entries extracts an association
list of library names and
org-babel-execute:LANGUAGE
function names. - Listing lst:org-babel-active-languages produces a list containing language
names that have
org-babel-execute:LANGUAGE
functions. - Listing lst:get-in-buffer-settings and lst:get-in-buffer-settings-result show how to access In-buffer Settings (info).
;; "Search failed:" with paren mismatching problems start here,
;; but the problems are not due to this source block.
(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)
#+caption[Org Babel libraries with org-babel-execute:language
functions]:
(defun org-babel-active-languages ()
(let (result)
(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-lessp))
(org-collect-keywords '("title" "author" "latex_class"))
(("TITLE" "Emacs setup for use with LaTeX, Lisp, Org, and Python")
("AUTHOR" "Gerard Vermeulen") ("LATEX_CLASS" "article"))
Citar: citing bibliography with Org Mode
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, 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 Citations in org-mode: Org-cite and Citar tries to walk
you from understanding the general context (bibliography producer, text
processor, text to product converter) to an Emacs setup. Listing
lst:set-oc+citar-options shows the oc
and citar
setup with binding of
(eval
to src_emacs-lisp{(call-interactively 'org-cite-insert)} inorg-mode-map
.
(when (ensure-package-installation 'citar 'citar-embark)
(with-eval-after-load 'embark
(when (fboundp 'citar-embark-mode)
(citar-embark-mode +1)))
(with-eval-after-load 'oc
(setopt org-cite-export-processors '((latex biblatex)
(t csl))
org-cite-global-bibliography '("~/VCS/research/refs.bib"))
(if (not (require 'citar nil 'noerror))
(user-error "Fails to require `citar' and to setup `citar' and `oc'")
;; Synchronize the two involved bibliographies.
(setopt citar-bibliography org-cite-global-bibliography
citar-library-file-extensions '("djvu" "pdf")
;; Here are the `.djvu' and `.pdf' files.
citar-library-paths '("~/VCS/research/papers/"))
(setopt org-cite-activate-processor 'citar
org-cite-follow-processor 'citar
org-cite-insert-processor 'citar)
(with-eval-after-load 'org
(keymap-set org-mode-map "C-c b" #'org-cite-insert)))))
Ref. [cite:@Schulte.MCSE.2011.41] shows that 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 show how to use Citar within this
setup, provided that citar-bibliography
and citar-library-paths
point to
valid directories and files. In an Org-mode buffer this setup allows to:
-
Insert one or more Org-mode cite links:
- Type .
-
Choose one or multiple citations from the bibliography:
- by navigating to an item and selecting it with
(eval
- by repeatingly navigating to an item and preselecting it with .
- by navigating to an item and selecting it with
-
View an electronic copy of an Org-mode cite link:
- Move point to the Org-mode cite link.
- Type .
- Navigate to the corresponding library file.
- Select it.
-
Open the DOI of an Org-mode cite link:
- Move point to the Org-mode cite link.
- Type .
- Navigate to the corresponding link.
- Select it.
-
The following
citar
functions may be useful too:- src_emacs-lisp{(call-interactively 'citar-insert-citation)}.
- src_emacs-lisp[:results silent]{(call-interactively 'citar-open)}.
TODO Compare bibtex and biblatex
- Using bibtex: a short guide
- The biblatex package
- Rebiber: A tool for normalizing bibtex with official info
- A biblatex implementation of the AIP and APS bibliography style
(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))))))
Engrave Faces
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 README.org and
the user-friendliness of this facility to a level acceptable for my workflow:
- 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-floatingtcolorbox
environments (those without captions). I have traced the non-equidistant interline spacing back to thebreakable
option ofCode
environments insidelisting
environments. However,tcolorbox
requiresbreakable
for listings that do not fit on a page. The filterorg-latex-engraved-source-block-filter
in listing lst:org-latex-engraved-source-block-filter and theorg-latex-engraved-preamble
customization in listing lst:smart-latex-engrave-org-source-blocks allow to engraveorg-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. - Listing lst:ox-engraved-emacs-lisp-setup shows how to use this configuration.
(ensure-package-installation 'engrave-faces)
(defun org-latex-engraved-source-block-filter (data _backend _info)
"Replace \"Code\" with \"Breakable\" in non-floating DATA 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)
;; Transform only blocks matching at position 0. Therefore, do
;; not transform blocks that are listing environments.
(when (string-match "\\`\\\\begin{Code}\n" data)
(setq data (replace-match "\\begin{Breakable}\n" t 'literal data))
(if (string-match "^\\\\end{Code}\n" data)
(setq data (replace-match "\\end{Breakable}\n" t 'literal data))
(error "Match `^\\\\end{Code}' failure")))))
(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}}
% Do not rely on an eventual call to `engrave-faces-latex-gen-preamble'.
\\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]")))
(with-eval-after-load 'ox-latex
(make-local-variable '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)))
Making Org hyperlink types (info)
The listings below implement or reimplement three groups of org-link
types:
- Listing lst:org-ref-like-org-link-types defines
org-link
types for backwards compatibility with org-ref. - Listing lst:define-org-pdfview-link-type uses ideas from the definition of
docview-org-link
anddoi-org-link
types to define anpdfview-org-link
type for use with pdf-tools. - 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 constantsorg-info-emacs-documents
andorg-info-other-documents
as buffer local variables in order to export theinfo-org-link
types in this document tohtml
and LaTeX correctly.
(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))))
(declare-function pdf-view-current-page "pdf-macs" (&optional window))
(declare-function pdf-view-goto-page "pdf-view" (page &optional window))
(defun org-pdfview-export (link description backend _)
"Export LINK as a \"pdfview\" type link using DESCRIPTION and export BACKEND.
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 "<a href=\"%s\">%s</a>" path desc))
(`latex (format "\\href{%s}{%s}" path desc))
(`ascii (format "%s (%s)" desc path))
(_ path)))))
(defun org-pdfview-open (link _)
"Open LINK as 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 to respect org-link-frame-setup.
(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))))
(with-eval-after-load 'ol
(org-link-set-parameters "pdfview"
:follow #'org-pdfview-open
:export #'org-pdfview-export
:store #'org-pdfview-store-link))
(declare-function org-info-map-html-url "ol-info" (filename))
(declare-function org-info--expand-node-name "ol-info" (node))
(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 "<a href=\"%s#%s\">%s</a>"
(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)))))
(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")
("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)))
Extending org-mode to handle YouTube links
Listing define org-yt-link type implements code to open the link to the following YouTube video Extending org-mode to handle YouTube links and the following Emacs setup.
Opening Extending org-mode to handle YouTube links may fail due to a bug in the
interface between mpv
and yt-dlp
. Listing set EMMS options indicates how to
work around this bug at the cost of making the user interface less clean.
However, the link Absolute Beginner's Guide to Emacs works always.
The following posts provide programming information:
;; https://bitspook.in/blog/extending-org-mode-to-handle-youtube-paths/
(declare-function emms-add-url "emms-source-file" (url))
(declare-function emms-playlist-last "emms" ())
(declare-function emms-playlist-mode-play-current-track "emms-playlist-mode" ())
(declare-function with-current-emms-playlist "emms" (&rest body))
(defun org-yt-emms-open (path)
"Open an \"YouTube\" PATH 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\" PATH 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\" PATH link according to DESC and BACKEND."
(pcase backend
(`ascii
(format "[%s] <https://www.youtube.com/watch?v=%s>" desc path))
(`html
(format "<iframe
frameborder=\"0\" width=\"600\" height=\"450\"
src=\"https://www.youtube.com/embed/%s\"
title=\"%s\"
allowfullscreen></iframe>" 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))
Translate capital keywords (old) to lower case (new)
(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))))))
Evaluate specific source blocks at load-time
How to do load time source block evaluation
(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)))
Org-mode macro utilities
Listing lst:by-backend-kbd-org-macro defines the Emacs Lisp utilities to
define the Org mode kbd
macro in listing
lst:source-file-export-keyword-settings.
(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
(ascii (format "`kbd(%s)'" keys))
(html (format "@@html:<kbd>%s</kbd>@@" (htmlize-protect-string keys)))
(latex (format "@@latex:\\colorbox{PowderBlue}{\\texttt{%s}}@@" keys)))))
Testing Org
Listing lst:batch-org-testing shows the recommended way: use make test
in a
clean Org git repository. Listing lst:setup-org-mode-test-1 and
lst:setup-org-mode-test-2 provide facilities for Testing Org interactively.
make test > out.txt 2>&1
(with-eval-after-load 'emacs
(defvar org-testing-dir nil)
(defvar org-testing-lisp-files nil)
(defun setup-org-mode-test ()
"Initialize an `org-mode' test."
(interactive)
(unless (and org-testing-dir org-testing-lisp-files)
(find-library "org.el")
(quit-window)
(setq org-testing-dir
(file-name-concat
(file-name-directory
(directory-file-name
(file-name-directory
(buffer-file-name (get-buffer "org.el")))))
(file-name-as-directory "testing")))
(setq org-testing-lisp-files
(seq-filter
(lambda (f)
(string-suffix-p ".el" f))
(directory-files
(file-name-concat org-testing-dir
(file-name-as-directory "lisp"))))))
(unless (fboundp 'org-test-with-temp-text)
(find-file (file-name-concat org-testing-dir "org-test.el"))
(eval-buffer)
(quit-window))
(let* ((test
(completing-read "Org-mode test: "
org-testing-lisp-files nil 'require-match)))
(find-file (file-name-concat
org-testing-dir (file-name-as-directory "lisp") test))
(eval-buffer)
(message "Evaluated '%s'"
(file-name-nondirectory (buffer-file-name (get-buffer test))))
(quit-window))))
(with-eval-after-load 'emacs
;; Stolen from `org-test-run-all-tests'.
(defun org-test-run-some-tests ()
"Run some defined tests.
Load all test files first."
(interactive)
(org-test-touch-all-examples)
(org-test-update-id-locations)
(org-test-load)
(let (;; Catch errors in diary sexps better.
(calendar-debug-sexp t))
;; test-org-element/normalize-contents fails (can't fix).
;; test-ob-exp/noweb-on-export fails due to spacing (can't fix).
;; test-ob-exp/noweb-on-export-with-exports-results due to spacing (idem).
;; ob-python hangs sometimes, but seldom.
;; ob-tangle/continued-code-blocks-w-noweb-ref fails.
(ert (rx (or "org/" "org-fold" "org-element" "org-macro" "org-src"
"property-inheritance"
"ob/" "ob-emacs-lisp" "ob-eshell"
"ob-python" "ob-tangle")))
(org-test-kill-all-examples)))
(defun org-test-run-ob-tests ()
"Run all \"ob\" tests.
Load all test files first."
(interactive)
(org-test-load)
(let (;; Catch errors in diary sexps better.
(calendar-debug-sexp t))
(ert "ob/")))
(defun org-test-run-a-test-file ()
"Run all tests in an Org test file (trailing `/' matters).
Examples: `ob/' or `ob-python'."
(interactive)
(let ((text (read-string "Org test file (without leading test-): "
nil nil "ob-python")))
(org-test-load)
(let (;; Catch errors in diary sexps better.
(calendar-debug-sexp t))
(ert text)))))
LaTex preamble editing using Noweb (info)
Note: How to include LaTeX packages in a LaTeX export block.
Listing lst:source-file-export-keyword-settings shows the preamble lines of this
/gav451/emacs.d/src/commit/2a23e9b796886a02b7d679107ebbd3ce364921a3/README.org file. It lists the export keyword settings, the definition of
the 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
contain the LaTeX preamble. All LaTeX listings in this section can be and are
handled by noweb (info).
<<latex-header-1>>
<<latex-header-2>>
<<latex-header-3>>
<<latex-header-4>>
<<latex-header-5>>
Deprecated LaTeX preamble editing methods
NOTE: This Org (info) file uses the recommended method of noweb trickery in Section #sec:file-inclusion-and-noweb.
There are at least two deprecated (old and historic) ways to edit the Org
#+latex_header:
and #+latex_extra_header:
keywords easily
for export to LaTeX, see LaTeX header and sectioning (info).
The old way – exploiting an idea of Omar Antolin Camarena – is to code new <LANGUAGE>-modes allowing to edit in LaTeX mode and to export to LaTeX code with correct LaTeX preamble export setting prefixes. Here, are links to three posts exposing his idea:
- Export LaTeX macros to LaTeX and HTML/MathJax preambles (reddit),
- Export JavaScript source blocks to script tags in HTML (reddit),
- 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 <LANGUAGE>-modes: latex-header
and latex-extra-header
.
The old way is to define two Org Babel languages (latex-extra-header
and
latex-header
) as done in Listing lst:org-babel-latex-header-blocks. The
historic way is to use a special export attribute as in the function
org-latex-header-blocks-filter
in 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
. This setup keeps 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 #+SETUPFILE: <FILE>, but only
in #+INCLUDE: <FILE> files.
(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.
Calling `org-babel-execute-src-block' calls this function 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.
Calling `org-babel-execute-src-block' calls this function 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))))
(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-functions))
HTML export (info)
The code label and link feature in Literal Examples (info) does not integrate with LaTeX export.
(with-eval-after-load 'ox-html
;; (Info-find-node "Org" "Literal Examples")
(setopt org-html-head-include-scripts t)
;; Force `org-index-capture' loading after lazy `ox-html' loading.
(require 'org-index-capture nil 'noerror))
Advanced LaTeX export settings
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
. Read the documentation strings to understand how
those variables control exporting from Org-mode to LaTeX.
(when (require 'ox-latex nil 'noerror)
;; https://emacs.stackexchange.com/questions/47347/
;; customizing-org-latex-title-command-to-edit-title-page
;; https://tex.stackexchange.com/questions/506102/
;; adding-header-and-footer-to-custom-titlepage
(make-local-variable '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)
(setq-local org-latex-title-command "\\begin{titlepage}
%% https://emacs.stackexchange.com/questions/47347/
%% customizing-org-latex-title-command-to-edit-title-page
%% 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}")
(setq-local org-latex-toc-command "
\\tableofcontents\\label{toc}
\\listoflistings
\\listoftables
\\newpage
")
(setq-local org-latex-subtitle-format ""))
Org Syntax
Two tools to grok how Org mode parsing works are the Org Syntax specification and the Org mode parser tutorial. The Org element parsing API boils down to three functions:
- The function
org-element-parse-buffer
implements a fully recursive buffer parser that returns an abstract syntax tree. - The functions
org-element-at-point
andorg-element-context
return information on the document structure around point either at the element level or at the object level in case oforg-element-context
.
Listing lst:grok-org-element-tree improves the 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.
(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 ()
"Call `org-element-at-point' interactively and pretty-print."
(interactive)
(pp-display-expression
(org-element-at-point) grok-org-output))
(defun grok-org-element-context ()
"Call `org-element-context' interactively and pretty-print."
(interactive)
(pp-display-expression
(org-element-context) grok-org-output))
(defun grok-org-element-parse-buffer ()
"Call `org-element-parse-buffer' interactively and pretty-print."
(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-element-parse-whole-buffer ()
"Like `grok-org-element-parse-buffer' from point 1 and sans granularity."
(interactive)
(org-with-point-at 1
(pp-display-expression (org-element-parse-buffer) grok-org-output)))
(defun grok-org-heading-components ()
"Call `org-heading-components' interactively and pretty-print."
(interactive)
(pp-display-expression
(org-heading-components) grok-org-output))
(defun grok-org-element-lineage ()
"Call `org-element-lineage' interactively and pretty-print."
(interactive)
(org-load-modules-maybe)
(pp-display-expression
(org-element-lineage (org-element-context) nil t) grok-org-output))))
Grammar, spelling, and style tools
Abbrevs (info)
Mickey Peterson has posted Correcting Typos and Misspellings with Abbrev showing
how to use Keyboard Macros (info) to exploit 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[:results silent]{(describe-variable 'global-abbrev-table)}
and to store it in src_emacs-lisp[:results silent]{(describe-variable
'abbrev-file-name)} to manage its changes with git. I can change the
abbreviation definitions in this file by means of:
- Execute src_emacs-lisp[:results silent]{(edit-abbrevs)} to alter abbreviation
definitions by editing an
*Abbrevs*
buffer. - Add, edit, or remove definitions of the form
"source" 1 "target"
under the global or a mode-specific table. - Execute src_emacs-lisp{(abbrev-edit-save-buffer)} to save all user abbreviation definitions in the current buffer.
(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))
TODO DICT.org local server fails on Darwin
Evaluating src_emacs-lisp{(dictionary)} connects to a local or remote dictd
server. The following links explain how to configure and use dictd
:
I am using the following Debian Bullseye dictionaries on Darwin and Gentoo:
- dict-devil_1.0-13.1_all.deb
- dict-foldoc_20201018-1_all.deb
- dict-gcide_0.48.5+nmu1_all.deb
- dict-jargon_4.4.7-3.1_all.deb
- dict-vera_1.24-1_all.deb
- dict-wn_3.0-36_all.deb
# https://jpmens.net/2020/03/08/looking-up-words-with-dict/
server 127.0.0.1 {
port 2628
}
# Local Variables:
# mode: conf-unix
# End:
cat > ~/.dictd.conf <<EOF
# https://www.masteringemacs.org/article/wordsmithing-in-emacs
database devil {
data "${HOME}/.local/share/dictd/devil.dict.dz"
index "${HOME}/.local/share/dictd/devil.index"
}
database foldoc {
data "${HOME}/.local/share/dictd/foldoc.dict.dz"
index "${HOME}/.local/share/dictd/foldoc.index"
}
database gcide {
data "${HOME}/.local/share/dictd/gcide.dict.dz"
index "${HOME}/.local/share/dictd/gcide.index"
}
database jargon {
data "${HOME}/.local/share/dictd/jargon.dict.dz"
index "${HOME}/.local/share/dictd/jargon.index"
}
database vera {
data "${HOME}/.local/share/dictd/vera.dict.dz"
index "${HOME}/.local/share/dictd/vera.index"
}
database wn {
data "${HOME}/.local/share/dictd/wn.dict.dz"
index "${HOME}/.local/share/dictd/wn.index"
}
EOF
# https://jpmens.net/2020/03/08/looking-up-words-with-dict/
/usr/local/sbin/dictd \
--config .dictd.conf \
--verbose \
--logfile .dictd.log \
-d nodetach
Writegood mode
Writegood mode is a minor mode to aid in finding common writing problems. The
post Matt Might's "My Ph.D. advisor rewrote himself in bash" scripts inspired
this mode. Listing lst:configure-writegood-mode configures writegood mode and
enables writegood-mode
for text-mode
buffers. Therefore, the best way use
it for this buffer is by typing
(eval
to export the it to atext-mode
buffer where the writegood-mode
faces are more visible.
(when (and (ensure-package-installation 'writegood-mode)
(fboundp 'writegood-mode))
(add-hook 'after-init-hook
(defun on-after-change-mode-hook-enable-writegood-mode ()
(add-hook 'after-change-major-mode-hook
(defun enable-writegood-mode ()
(when (derived-mode-p '(org-mode text-mode))
(writegood-mode +1)))))
'depth)
(global-set-key (kbd "C-c g") #'writegood-mode))
Which-function-mode (info)
Listing lst:setup-which-function-mode sets which-function-mode
options. The
post Emacs: which-function-mode claims that which-function-mode
works in
org-mode
. This is true in case all document headlines are simple, but is not
true in case document headlines contain links. The code in listing
lst:define-for-which-func-functions has stolen ideas from the following links:
(with-eval-after-load 'which-func
(setopt which-func-modes
'(emacs-lisp-mode org-mode pdf-view-mode)))
;; It looks like `python-mode' does nothing when it is an element of
;; `which-func-modes'.
;; (setopt which-func-modes t)
;; (setopt which-func-display 'header) ;; AFAIU, fails on my system.
;; https://emacs.stackexchange.com/questions/30894/
(declare-function pdf-info-outline "pdf-info" (&optional file-or-buffer))
(declare-function org-element-type-p "org-element-ast" (node types))
(defvar which-func-functions nil)
(defun which-func-org-function ()
"Return level and title of the current headline.
Return the document title when point is above the first headline."
(interactive) ;; Keep this function interactive for fixing.
(when (eq major-mode 'org-mode)
(let (text)
;; Handle point eventually above the first headline.
(unless (org-at-heading-p)
(save-excursion
(org-previous-visible-heading 1)
(let ((eap (org-element-at-point)))
(when (org-element-type-p eap 'keyword)
(setq text (format "0|%s" (org-element-property :value eap)))))))
;; Handle point anywhere else.
(unless text
(let* ((chain (org-get-outline-path t))
(count (length chain)))
(setq text (format "%s|%s" count (nth (1- count) chain)))))
text)))
(add-to-list 'which-func-functions 'which-func-org-function)
(defun which-func-pdf-view-function ()
"Return the title of the current headline.
Return \"Front Matter\" when current page is above the first headline."
(interactive) ;; Keep this function interactive for fixing.
(when (eq major-mode 'pdf-view-mode)
(let* ((current-page (pdf-view-current-page))
(outline (pdf-info-outline (current-buffer)))
(hl-count (length outline))
(hl-index 0)
(hl-page 0)
(old-title "Front Matter")
(new-title old-title))
;; Return the first headline on a page which is better than nothing.
;; I can't do better, since `pdf-view-mode' has no notion of point.
(while (and (< hl-index hl-count) (< hl-page current-page))
(setq old-title new-title)
(setq hl-page (alist-get 'page (nth hl-index outline)))
(setq new-title (alist-get 'title (nth hl-index outline)))
(cl-incf hl-index))
(if (< current-page hl-page) old-title new-title))))
(add-to-list 'which-func-functions 'which-func-pdf-view-function)
Saving Emacs sessions (info)
Listing setup desktop makes Emacs save its state between closing its session to opening the next session.
(with-eval-after-load 'emacs
;; Load and configure `Org' before enabling `desktop-save-mode'.
(desktop-save-mode t)
(setopt desktop-buffers-not-to-save
(rx bos (or "*Apropos" "compilation*"))
desktop-modes-not-to-save
'(emacs-lisp-mode image-mode tags-table-mode)))
BUG: I fail to code a function to set the desktop-buffers-not-to-save-function
option.
Programming Tools
Eglot (info)
Emacs polyGLOT (Eglot) is an Emacs language-server-protocol client that stays out of your way. Eglot (info) is a builtin since Emacs-29.1. The following listings contribute to a programming language mode independent Eglot configuration:
- Listing minimal Eglot setup adds key bindings to
eglot-mode-keymap
. - Listing lst:setup-eglot-jl adds Julia support to Eglot (info).
- Listing lst:cli-eglot-jl-setup shows how to setup eglot-jl from the command line.
- Listing lst:setup-oglot uses oglot to enable using Eglot in
org-src-mode
Julia and Python buffers. - Listing lst:eglot-maybe-ensure starts Eglot in case of proper programming modes and proper directory local variables (meaning in presence of a proper file .dir-locals.el in the root directory of any project using proper programming modes).
- Listing lst:whiten-black defines Emacs Lisp functions to undo (whiten) some output of Black after src_emacs-lisp[:results silent]{(org-babel-tangle)}.
(with-eval-after-load 'eglot
(keymap-set eglot-mode-map "C-c n" 'flymake-goto-next-error)
(keymap-set eglot-mode-map "C-c p" 'flymake-goto-prev-error)
(keymap-set eglot-mode-map "C-c r" 'eglot-rename))
;; How to invoke `imenu-add-menubar-index' automatically?
;; Neither advising `eglot' nor hooking major modes seems to work.
(when (ensure-package-installation 'eglot-jl)
(eglot-jl-init))
# Run this from `package-user-dir':
julia --project=eglot-jl-20240318.1159/ eglot-jl-20240318.1159/eglot-jl.jl
# The SymbolServer is ready caching dependencies when it displays:
# [ Info: Received new data from Julia Symbol Server.
(when (and (package-installed-p 'oglot)
(require 'oglot nil 'noerror))
(setopt oglot-maybe-ensure-modes '(julia-mode python-mode)))
(defun eglot-maybe-ensure ()
"Call `eglot-ensure' under favorable conditions."
(when (and (apply #'derived-mode-p '(python-mode))
(assoc 'eglot-workspace-configuration dir-local-variables-alist))
(eglot-ensure)))
;; The two hooks `after-change-major-mode-hook' and
;; `hack-local-variables-hook' are OK, but language mode hooks like
;; `python-mode-hook' are not.
(add-hook 'after-change-major-mode-hook #'eglot-maybe-ensure)
(with-eval-after-load 'emacs
(defun black-python-sequence ()
"Try to find a Blackened Python sequence to whiten."
(interactive)
(if (derived-mode-p 'python-mode)
(re-search-forward "[[(][ \t]*$" nil t)
(org-narrow-to-block)
(re-search-forward "[[(][ \t]*$" nil t)
(widen)))
(defun whiten--python-sequence ()
"Whiten a Blackened Python sequence without narrowing.
Call `widen' after an `user-error'."
(if (not (and (looking-at-p "[ \t]*$")))
(user-error "Call `widen': not seeing `new-line'")
(skip-chars-backward " \t\\[\\(")
(if (not (looking-at-p "[ \t]*[[(][ \t]*$"))
(user-error "Call `widen': not seeing `[' or `(' before `new-line'")
(fixup-whitespace)
(skip-chars-forward " \\[\\(")
(kill-line)
(fixup-whitespace)
(move-end-of-line 1)
(while (and (eq ?\, (char-before)))
(kill-line)
(fixup-whitespace)
(move-end-of-line 1))
(save-excursion
(when (memq (char-before) '(?\) ?\]))
(goto-char (1- (point)))
(delete-char -1))))))
(defun whiten-python-sequence ()
"Whiten a Blackened Python sequence. Call `widen' after an `user-error'."
(interactive)
(if (derived-mode-p 'python-mode)
(whiten--python-sequences)
(org-narrow-to-block)
(whiten--python-sequence)
(widen))))
Format-all
Listing lst:configure-format-all:
- Configures format-all which is a package that provides an universal interface to code formatters of more than 60 computer languages.
- Adds
format-all-org-babel-post-tangle
toorg-babel-post-tangle-hook
to format tangled Python code.
;; https://github.com/lassik/emacs-format-all-the-code#readme
;; https://ianyepan.github.io/posts/format-all/
;; https://jamesaimonetti.com/posts/formatting-tangled-output-in-org-mode/
(when (ensure-package-installation 'format-all)
;; `format-all' defines `format-all-buffer' as an autoloaded command.
(with-eval-after-load 'ob-tangle
(add-hook
'org-babel-post-tangle-hook
(defun format-all-org-babel-post-tangle ()
(when (derived-mode-p 'python-mode)
(setq-local format-all-formatters '(("Python" black)))
(format-all-buffer)
(save-buffer)
(message "Saved reformatted tangled buffer `%s'"
(buffer-file-name)))))))
Flymake (info)
Flymake is an universal on-the-fly syntax checker for Emacs. It is a requirement
of eglot, but you can use it without eglot for instance in python-mode buffers
that do not visit a file. Listing lst:configure-flymake aliases list-errors
(new) to flymake-show-buffer-diagnostics
(old).
(with-eval-after-load 'flymake
(defalias 'list-errors #'flymake-show-buffer-diagnostics
"Show a list of Flymake diagnostics for current buffer.")
(keymap-set flymake-mode-map "M-n" 'flymake-goto-next-error)
(keymap-set flymake-mode-map "M-p" 'flymake-goto-prev-error))
Programming Modes
Common Lisp programming
Links to Common Lisp books and posts are:
- Common Lisp
- Kvardek Du - Luis Oliveira
- Lisp Resources
- On Lisp - Paul Graham
- Traité de Programmation en Common Lisp
Sly
Listing lst:configure-sly configures the Sly (info) Common Lisp IDE for Emacs for use with Steel Bank Common Lisp (sbcl):
- It configures
sly-default-lisp
andsly-lisp-implementations
as in thesly
documentation string instead of in the multiple lisps (info) manual. - It does not ensure the automatic connection to the lisp server (info) when
opening a Common Lisp file because it does not play well with
ob-lisp
. - It configures searching documentation in the Common Lisp HyperSpec according to the basic customization (info) manual.
Listing lst:slink-sel allows to set or get the Sly string elision length. Finally, listing lst:configure-sly uses a technique to load Slynk faster (info) by means of a custom core file in src_emacs-lisp[:results silent]{(describe-variable 'no-littering-var-directory)}. Listing lst:sbcl-core-for-sly tangles to a script to dump such a SBCL core.
(when (ensure-package-installation 'sly 'sly-macrostep 'sly-named-readtables)
(with-eval-after-load 'sly
;; Set `sly-default-lisp' instead of `inferior-lisp-program',
;; because `sly' uses `inferior-lisp-program' only as a backwards
;; compatibility fallback option.
;; The warning "Value ‘sbcl’ does not match type function" is due to
;; the buggy `defcustom' type of `sbcl-default-lisp' in `sly.el'.
(setopt sly-db-initial-restart-limit 10
sly-default-lisp 'sbcl
sly-lisp-implementations
`((ccl (,(executable-find "ccl64")))
(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")))
(keymap-set sly-prefix-map "M-h" #'sly-documentation-lookup)))
#+caption[Get/set Sly slynk:*string-elision-length*
functionality]:
(with-eval-after-load 'sly
(defconst slynk-command-get-sel
"(cdr (assoc 'slynk:*string-elision-length* slynk:*slynk-pprint-bindings*))"
"Slynk \"string elision length\" getter command.")
(defun slynk-command-set-sel (length)
"Slynk LENGTH \"string elision length\" setter command.
LENGTH values must be positive integers to enable or `nil' to disable elision."
(format "(setf %s %s)" slynk-command-get-sel length))
(defun slynk-get-sel ()
"Get the Slynk \"string-elision-length\"."
(cadr (sly-eval `(slynk:eval-and-grab-output ,slynk-command-get-sel))))
;; Most useful for Org source blocks.
(defun slynk-set-sel (length)
"Set the Slynk \"string-elision-length\" LENGTH.
LENGTH values must be positive integers to enable or `nil' to disable elision."
(cadr
(sly-eval `(slynk:eval-and-grab-output ,(slynk-command-set-sel length)))))
;; Most useful for interactive use.
(defun slynk-eval-sel-command (string)
"Evaluate the Slynk \"string-elision-length\" command STRING."
(unless (sly-connected-p)
(user-error "Slynk connection is not open: \"M-x sly\"?"))
(message "Slynk string elision length is %S"
(read (cadr (sly-eval `(slynk:eval-and-grab-output ,string))))))
(defun slynk-get-string-elision-length ()
"Get the Slynk \"string-elision-length\"."
(interactive)
(slynk-eval-sel-command slynk-command-get-sel))
(defun slynk-set-string-elision-length (&optional length)
"Set the Slynk \"string-elision-length\" LENGTH.
Valid LENGTH values are positive integers to set or `nil' to reset."
(interactive "X\ Slink string elision length: ")
(unless (or (null length) (and (integerp length) (> length 0)))
(user-error
"Slynk `length' must be `nil' or a positive integer (got `%S')" length))
(slynk-eval-sel-command (slynk-command-set-sel length))))
#!/bin/sh
sbcl <<EOF
(mapc 'require '(sb-bsd-sockets sb-posix sb-introspect sb-cltl2 asdf))
(save-lisp-and-die "sbcl.core-for-sly")
EOF
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# sh-basic-offset: 2
# End:
CS 325 AI Programming
The CS 325 AI Programming course allows to learn Common Lisp by self-study and the page CS 325: Setting up Lisp gives instructions how to:
Quicklisp
Quicklisp is a library manager for Common Lisp. Listing lst:download+verify-quicklisp downloads the Quicklisp installation file and verifies its signature to prevent tampering. Listing lst:bootstrap-quicklisp tangles to a shell script allowing to bootstrap Quicklisp with SBCL. Listing lst:quicklisp-sbclrc-file tangles to the a SBCL resource file with Quicklisp support. Listing lst:clone-local-projects shows how to make local projects out of unpackaged projects and listing lst:register-load-local-projects shows how to register local projects in order to load those unpackaged projects in the same way as packaged projects.
curl -sS -O https://beta.quicklisp.org/quicklisp.lisp
curl -sS -O https://beta.quicklisp.org/quicklisp.lisp.asc
curl -sS -O https://beta.quicklisp.org/release-key.txt
gpg --import release-key.txt
gpg --verify quicklisp.lisp.asc quicklisp.lisp
#!/bin/sh
sbcl --load ~/quicklisp.lisp <<EOF
(quicklisp-quickstart:install)
(quit)
EOF
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# sh-basic-offset: 2
# End:
git clone https://gitlab.com/criesbeck/cs325.git
git clone git@github.com:ageldama/cl-state-machine.git
;; SBCL on Darwin fails to run cl-cffi-gtk:
;; https://lisp-journey.gitlab.io/blog/gui-programming-in-common-lisp-part-3-of-5-gtk3/
;; https://www.crategus.com/books/cl-cffi-gtk/
;; https://www.crategus.com/books/cl-gtk/gtk-tutorial.html
(defun probe--local-project-directory (name)
(some #'identity (mapcar (lambda (x)
(let ((*default-pathname-defaults* x))
(probe-file name)))
ql:*local-project-directories*)))
(when (probe--local-project-directory "cs325")
(ql:register-local-projects)
(ql:quickload "cs325")
(ql:quickload "meta")
(ql:quickload '("named-readtables" "try")) ;; testing requires "try".
#-:clozure
(ql:quickload "nodgui") ;; requires https://www.tcl.tk/software/tklib/
(ql:quickload '("rutils" "rutilsx"))
(ql:quickload "ucons"))
(when (probe--local-project-directory "cl-state-machine")
(ql:quickload '("cl-state-machine" "cl-state-machine-examples"
"cl-state-machine-graphing" "cl-state-machine-test")))
;;; Hey Emacs, this is my -*- lisp -*- .sbclrc file.
;;; The following lines added by ql:add-to-init-file:
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
;;; Load cs325.lisp to create the cs325 package.
;; (eval-when (:compile-toplevel :load-toplevel :execute)
;; (ql:quickload "cs325")
;; (setq *package* (find-package :cs325-user)))
Lisp mode custom font locking for Common Lisp
This section implements the code described in the Emacs Common Lisp Font Locking post.
Initialize Common Lisp custom font locking
;;; Common Lisp custom font lock configuration.
;;; https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/
(defface cl-character-face
'((default :inherit font-lock-constant-face))
"The face used to highlight Common Lisp character literals.")
(defface cl-standard-function-face
'((default :inherit font-lock-keyword-face))
"The face used to highlight standard Common Lisp function symbols.")
(defface cl-standard-value-face
'((default :inherit font-lock-variable-name-face))
"The face used to highlight standard Common Lisp value symbols.")
(defun standard-symbol-names (predicate)
(let ((symbols nil))
(do-external-symbols (symbol :common-lisp)
(when (funcall predicate symbol)
(push (string-downcase (symbol-name symbol)) symbols)))
(sort symbols #'string<)))
#+caption[Set Sly ~slynk:*string-elision-length*~]:
(when (and (fboundp 'sly-connected-p) (sly-connected-p))
(and (fboundp 'slynk-set-sel) (slynk-set-sel (ash 1 14))))
#+caption[Set Sly slynk:*string-elision-length*
result]:
16384 (15 bits, #x4000)
Collect standard symbol function names
(format nil "(defvar~% cl-function-names~% '~S)~%"
(standard-symbol-names #'fboundp))
(defvar
cl-function-names
'("*" "+" "-" "/" "/=" "1+" "1-" "<" "<=" "=" ">" ">=" "abort" "abs" "acons"
"acos" "acosh" "add-method" "adjoin" "adjust-array" "adjustable-array-p"
"allocate-instance" "alpha-char-p" "alphanumericp" "and" "append" "apply"
"apropos" "apropos-list" "aref" "arithmetic-error-operands"
"arithmetic-error-operation" "array-dimension" "array-dimensions"
"array-displacement" "array-element-type" "array-has-fill-pointer-p"
"array-in-bounds-p" "array-rank" "array-row-major-index" "array-total-size"
"arrayp" "ash" "asin" "asinh" "assert" "assoc" "assoc-if" "assoc-if-not"
"atan" "atanh" "atom" "bit" "bit-and" "bit-andc1" "bit-andc2" "bit-eqv"
"bit-ior" "bit-nand" "bit-nor" "bit-not" "bit-orc1" "bit-orc2"
"bit-vector-p" "bit-xor" "block" "boole" "both-case-p" "boundp" "break"
"broadcast-stream-streams" "butlast" "byte" "byte-position" "byte-size"
"caaaar" "caaadr" "caaar" "caadar" "caaddr" "caadr" "caar" "cadaar"
"cadadr" "cadar" "caddar" "cadddr" "caddr" "cadr" "call-method" "car"
"case" "catch" "ccase" "cdaaar" "cdaadr" "cdaar" "cdadar" "cdaddr" "cdadr"
"cdar" "cddaar" "cddadr" "cddar" "cdddar" "cddddr" "cdddr" "cddr" "cdr"
"ceiling" "cell-error-name" "cerror" "change-class" "char" "char-code"
"char-downcase" "char-equal" "char-greaterp" "char-int" "char-lessp"
"char-name" "char-not-equal" "char-not-greaterp" "char-not-lessp"
"char-upcase" "char/=" "char<" "char<=" "char=" "char>" "char>="
"character" "characterp" "check-type" "cis" "class-name" "class-of"
"clear-input" "clear-output" "close" "clrhash" "code-char" "coerce"
"compile" "compile-file" "compile-file-pathname" "compiled-function-p"
"compiler-macro-function" "complement" "complex" "complexp"
"compute-applicable-methods" "compute-restarts" "concatenate"
"concatenated-stream-streams" "cond" "conjugate" "cons" "consp"
"constantly" "constantp" "continue" "copy-alist" "copy-list"
"copy-pprint-dispatch" "copy-readtable" "copy-seq" "copy-structure"
"copy-symbol" "copy-tree" "cos" "cosh" "count" "count-if" "count-if-not"
"ctypecase" "decf" "declaim" "decode-float" "decode-universal-time"
"defclass" "defconstant" "defgeneric" "define-compiler-macro"
"define-condition" "define-method-combination" "define-modify-macro"
"define-setf-expander" "define-symbol-macro" "defmacro" "defmethod"
"defpackage" "defparameter" "defsetf" "defstruct" "deftype" "defun"
"defvar" "delete" "delete-duplicates" "delete-file" "delete-if"
"delete-if-not" "delete-package" "denominator" "deposit-field" "describe"
"describe-object" "destructuring-bind" "digit-char" "digit-char-p"
"directory" "directory-namestring" "disassemble" "do" "do*"
"do-all-symbols" "do-external-symbols" "do-symbols" "documentation"
"dolist" "dotimes" "dpb" "dribble" "ecase" "echo-stream-input-stream"
"echo-stream-output-stream" "ed" "eighth" "elt" "encode-universal-time"
"endp" "enough-namestring" "ensure-directories-exist"
"ensure-generic-function" "eq" "eql" "equal" "equalp" "error" "etypecase"
"eval" "eval-when" "evenp" "every" "exp" "export" "expt" "fboundp"
"fceiling" "fdefinition" "ffloor" "fifth" "file-author"
"file-error-pathname" "file-length" "file-namestring" "file-position"
"file-string-length" "file-write-date" "fill" "fill-pointer" "find"
"find-all-symbols" "find-class" "find-if" "find-if-not" "find-method"
"find-package" "find-restart" "find-symbol" "finish-output" "first" "flet"
"float" "float-digits" "float-precision" "float-radix" "float-sign"
"floatp" "floor" "fmakunbound" "force-output" "format" "formatter" "fourth"
"fresh-line" "fround" "ftruncate" "funcall" "function" "function-keywords"
"function-lambda-expression" "functionp" "gcd" "gensym" "gentemp" "get"
"get-decoded-time" "get-dispatch-macro-character" "get-internal-real-time"
"get-internal-run-time" "get-macro-character" "get-output-stream-string"
"get-properties" "get-setf-expansion" "get-universal-time" "getf" "gethash"
"go" "graphic-char-p" "handler-bind" "handler-case" "hash-table-count"
"hash-table-p" "hash-table-rehash-size" "hash-table-rehash-threshold"
"hash-table-size" "hash-table-test" "host-namestring" "identity" "if"
"ignore-errors" "imagpart" "import" "in-package" "incf"
"initialize-instance" "input-stream-p" "inspect" "integer-decode-float"
"integer-length" "integerp" "interactive-stream-p" "intern" "intersection"
"invalid-method-error" "invoke-debugger" "invoke-restart"
"invoke-restart-interactively" "isqrt" "keywordp" "labels" "lambda" "last"
"lcm" "ldb" "ldb-test" "ldiff" "length" "let" "let*"
"lisp-implementation-type" "lisp-implementation-version" "list" "list*"
"list-all-packages" "list-length" "listen" "listp" "load"
"load-logical-pathname-translations" "load-time-value" "locally" "log"
"logand" "logandc1" "logandc2" "logbitp" "logcount" "logeqv"
"logical-pathname" "logical-pathname-translations" "logior" "lognand"
"lognor" "lognot" "logorc1" "logorc2" "logtest" "logxor" "long-site-name"
"loop" "loop-finish" "lower-case-p" "machine-instance" "machine-type"
"machine-version" "macro-function" "macroexpand" "macroexpand-1" "macrolet"
"make-array" "make-broadcast-stream" "make-concatenated-stream"
"make-condition" "make-dispatch-macro-character" "make-echo-stream"
"make-hash-table" "make-instance" "make-instances-obsolete" "make-list"
"make-load-form" "make-load-form-saving-slots" "make-package"
"make-pathname" "make-random-state" "make-sequence" "make-string"
"make-string-input-stream" "make-string-output-stream" "make-symbol"
"make-synonym-stream" "make-two-way-stream" "makunbound" "map" "map-into"
"mapc" "mapcan" "mapcar" "mapcon" "maphash" "mapl" "maplist" "mask-field"
"max" "member" "member-if" "member-if-not" "merge" "merge-pathnames"
"method-combination-error" "method-qualifiers" "min" "minusp" "mismatch"
"mod" "muffle-warning" "multiple-value-bind" "multiple-value-call"
"multiple-value-list" "multiple-value-prog1" "multiple-value-setq"
"name-char" "namestring" "nbutlast" "nconc" "nintersection" "ninth"
"no-applicable-method" "no-next-method" "not" "notany" "notevery" "nreconc"
"nreverse" "nset-difference" "nset-exclusive-or" "nstring-capitalize"
"nstring-downcase" "nstring-upcase" "nsublis" "nsubst" "nsubst-if"
"nsubst-if-not" "nsubstitute" "nsubstitute-if" "nsubstitute-if-not" "nth"
"nth-value" "nthcdr" "null" "numberp" "numerator" "nunion" "oddp" "open"
"open-stream-p" "or" "output-stream-p" "package-error-package"
"package-name" "package-nicknames" "package-shadowing-symbols"
"package-use-list" "package-used-by-list" "packagep" "pairlis"
"parse-integer" "parse-namestring" "pathname" "pathname-device"
"pathname-directory" "pathname-host" "pathname-match-p" "pathname-name"
"pathname-type" "pathname-version" "pathnamep" "peek-char" "phase" "plusp"
"pop" "position" "position-if" "position-if-not" "pprint" "pprint-dispatch"
"pprint-exit-if-list-exhausted" "pprint-fill" "pprint-indent"
"pprint-linear" "pprint-logical-block" "pprint-newline" "pprint-pop"
"pprint-tab" "pprint-tabular" "prin1" "prin1-to-string" "princ"
"princ-to-string" "print" "print-not-readable-object" "print-object"
"print-unreadable-object" "probe-file" "proclaim" "prog" "prog*" "prog1"
"prog2" "progn" "progv" "provide" "psetf" "psetq" "push" "pushnew" "quote"
"random" "random-state-p" "rassoc" "rassoc-if" "rassoc-if-not" "rational"
"rationalize" "rationalp" "read" "read-byte" "read-char"
"read-char-no-hang" "read-delimited-list" "read-from-string" "read-line"
"read-preserving-whitespace" "read-sequence" "readtable-case" "readtablep"
"realp" "realpart" "reduce" "reinitialize-instance" "rem" "remf" "remhash"
"remove" "remove-duplicates" "remove-if" "remove-if-not" "remove-method"
"remprop" "rename-file" "rename-package" "replace" "require" "rest"
"restart-bind" "restart-case" "restart-name" "return" "return-from"
"revappend" "reverse" "room" "rotatef" "round" "row-major-aref" "rplaca"
"rplacd" "sbit" "scale-float" "schar" "search" "second" "set"
"set-difference" "set-dispatch-macro-character" "set-exclusive-or"
"set-macro-character" "set-pprint-dispatch" "set-syntax-from-char" "setf"
"setq" "seventh" "shadow" "shadowing-import" "shared-initialize" "shiftf"
"short-site-name" "signal" "signum" "simple-bit-vector-p"
"simple-condition-format-arguments" "simple-condition-format-control"
"simple-string-p" "simple-vector-p" "sin" "sinh" "sixth" "sleep"
"slot-boundp" "slot-exists-p" "slot-makunbound" "slot-missing"
"slot-unbound" "slot-value" "software-type" "software-version" "some"
"sort" "special-operator-p" "sqrt" "stable-sort" "standard-char-p" "step"
"store-value" "stream-element-type" "stream-error-stream"
"stream-external-format" "streamp" "string" "string-capitalize"
"string-downcase" "string-equal" "string-greaterp" "string-left-trim"
"string-lessp" "string-not-equal" "string-not-greaterp" "string-not-lessp"
"string-right-trim" "string-trim" "string-upcase" "string/=" "string<"
"string<=" "string=" "string>" "string>=" "stringp" "sublis" "subseq"
"subsetp" "subst" "subst-if" "subst-if-not" "substitute" "substitute-if"
"substitute-if-not" "subtypep" "svref" "sxhash" "symbol-function"
"symbol-macrolet" "symbol-name" "symbol-package" "symbol-plist"
"symbol-value" "symbolp" "synonym-stream-symbol" "tagbody" "tailp" "tan"
"tanh" "tenth" "terpri" "the" "third" "throw" "time" "trace"
"translate-logical-pathname" "translate-pathname" "tree-equal" "truename"
"truncate" "two-way-stream-input-stream" "two-way-stream-output-stream"
"type-error-datum" "type-error-expected-type" "type-of" "typecase" "typep"
"unbound-slot-instance" "unexport" "unintern" "union" "unless"
"unread-char" "untrace" "unuse-package" "unwind-protect"
"update-instance-for-different-class" "update-instance-for-redefined-class"
"upgraded-array-element-type" "upgraded-complex-part-type" "upper-case-p"
"use-package" "use-value" "user-homedir-pathname" "values" "values-list"
"vector" "vector-pop" "vector-push" "vector-push-extend" "vectorp" "warn"
"when" "wild-pathname-p" "with-accessors" "with-compilation-unit"
"with-condition-restarts" "with-hash-table-iterator"
"with-input-from-string" "with-open-file" "with-open-stream"
"with-output-to-string" "with-package-iterator" "with-simple-restart"
"with-slots" "with-standard-io-syntax" "write" "write-byte" "write-char"
"write-line" "write-sequence" "write-string" "write-to-string" "y-or-n-p"
"yes-or-no-p" "zerop"))
Collect standard symbol value names
(format nil "(defvar~% cl-value-names~% '~S)~%"
(standard-symbol-names #'boundp))
(defvar
cl-value-names
'("*" "**" "***" "*break-on-signals*" "*compile-file-pathname*"
"*compile-file-truename*" "*compile-print*" "*compile-verbose*"
"*debug-io*" "*debugger-hook*" "*default-pathname-defaults*"
"*error-output*" "*features*" "*gensym-counter*" "*load-pathname*"
"*load-print*" "*load-truename*" "*load-verbose*" "*macroexpand-hook*"
"*modules*" "*package*" "*print-array*" "*print-base*" "*print-case*"
"*print-circle*" "*print-escape*" "*print-gensym*" "*print-length*"
"*print-level*" "*print-lines*" "*print-miser-width*"
"*print-pprint-dispatch*" "*print-pretty*" "*print-radix*"
"*print-readably*" "*print-right-margin*" "*query-io*" "*random-state*"
"*read-base*" "*read-default-float-format*" "*read-eval*" "*read-suppress*"
"*readtable*" "*standard-input*" "*standard-output*" "*terminal-io*"
"*trace-output*" "+" "++" "+++" "-" "/" "//" "///" "array-dimension-limit"
"array-rank-limit" "array-total-size-limit" "boole-1" "boole-2" "boole-and"
"boole-andc1" "boole-andc2" "boole-c1" "boole-c2" "boole-clr" "boole-eqv"
"boole-ior" "boole-nand" "boole-nor" "boole-orc1" "boole-orc2" "boole-set"
"boole-xor" "call-arguments-limit" "char-code-limit" "double-float-epsilon"
"double-float-negative-epsilon" "internal-time-units-per-second"
"lambda-list-keywords" "lambda-parameters-limit"
"least-negative-double-float" "least-negative-long-float"
"least-negative-normalized-double-float"
"least-negative-normalized-long-float"
"least-negative-normalized-short-float"
"least-negative-normalized-single-float" "least-negative-short-float"
"least-negative-single-float" "least-positive-double-float"
"least-positive-long-float" "least-positive-normalized-double-float"
"least-positive-normalized-long-float"
"least-positive-normalized-short-float"
"least-positive-normalized-single-float" "least-positive-short-float"
"least-positive-single-float" "long-float-epsilon"
"long-float-negative-epsilon" "most-negative-double-float"
"most-negative-fixnum" "most-negative-long-float"
"most-negative-short-float" "most-negative-single-float"
"most-positive-double-float" "most-positive-fixnum"
"most-positive-long-float" "most-positive-short-float"
"most-positive-single-float" "multiple-values-limit" "nil" "pi"
"short-float-epsilon" "short-float-negative-epsilon" "single-float-epsilon"
"single-float-negative-epsilon" "t"))
Finalize Common Lisp custom font locking
(defvar cl-font-lock-keywords
(let* ((character-re (concat "#\\\\" lisp-mode-symbol-regexp "\\_>"))
(function-re (concat "(" (regexp-opt cl-function-names t) "\\_>"))
(value-re (regexp-opt cl-value-names 'symbols)))
`((,character-re . 'cl-character-face)
(,function-re
(1 'cl-standard-function-face))
(,value-re . 'cl-standard-value-face))))
(defvar cl-font-lock-defaults
'((cl-font-lock-keywords)
nil ; enable syntaxic highlighting
t ; case insensitive highlighting
nil ; use the lisp-mode syntax table
(font-lock-mark-block-function . mark-defun)
(font-lock-extra-managed-props help-echo)
(font-lock-syntactic-face-function
. lisp-font-lock-syntactic-face-function)))
(defun cl-init-lisp-font-lock ()
"Hook to initialize Common Lisp font locking."
(setq font-lock-defaults cl-font-lock-defaults))
(add-hook 'lisp-mode-hook 'cl-init-lisp-font-lock)
Emacs Lisp Programming (info)
Here is a list of links describing how to program and debug Emacs Lisp (info) code:
- Evaluating Elisp in Emacs
- Debugging Elisp Part 1: Earn your independence
- Debugging Elisp Part 2: Advanced topics
- Xah talk show: Elisp coding: xah-add-space-after-comma
- Xah talk show: Elisp coding: narrow-to-region, sort-lines, hilight-unicode
Ref. [cite:@Monnier.ACM-PL.4.1] exposes the evolution of Emacs Lisp and explains important Emacs Lisp idioms.
Listing setup ielm configures the Interactive Emacs Lisp Mode for better interoperability with Smartparens: get help on the key bindings by means of src_emacs-lisp[:results silent]{(describe-function 'ielm)}.
(with-eval-after-load 'ielm
(setopt ielm-dynamic-return nil))
Debugging Emacs Lisp (info)
Listing edebug example from the source level debugger (edebug) wiki entry is a
minimal example of how to use edebug
:
- Type
(eval
to open itsorg-src-mode
buffer. - Type
(eval
with point infoo
. - Type
(eval
with point inbar
. - Evaluate
(foo)
. - Type
(eval
with point inbar
. - Evaluate
(foo)
. - Type the space bar to step through
bar
.
Note: I fail to instrument functions as in Edebug (info).
(defun foo ()
(interactive)
(bar))
(defun bar ()
(let ((a 5)
(b 7))
(message "%d" (+ a b))))
Writing Better Elisp Docstrings
;; Stolen from Charles Choi:
;; http://yummymelon.com/devnull/writing-better-elisp-docstrings.html
;; GAV: Org changes the semantics of calling `beginning-of-defun'.
(defun describe-function-at-point ()
"Call `describe-function' on the Elisp function at point.
Switch temporarily from `org-mode' to `text-mode', since `org-mode'
changes the semantics of calling `beginning-of-defun'."
(interactive)
(let ((mode major-mode))
(when (eq major-mode 'org-mode)
(text-mode))
(save-excursion
(beginning-of-defun)
(forward-char)
(forward-sexp 2)
(let ((end-point (point)))
(backward-sexp)
(let* ((fn-name (buffer-substring (point) end-point))
(interned (intern-soft fn-name)))
(if interned
(describe-function interned)
(message "Can't find function at point")))))
(when (eq mode 'org-mode)
(org-mode))))
(with-eval-after-load 'org-mode
(keymap-set org-mode-map "C-c d" #'describe-function-at-point))
(with-eval-after-load 'emacs-lisp-mode
(keymap-set emacs-lisp-mode-map "C-c d" #'describe-function-at-point))
Go Programming
Links for further investigation are:
- Go by example is a showcase of short examples with explanations.
(when (featurep 'treesit)
;; Set `tab-width' in `after-change-major-mode-hook' function too.
(setopt go-ts-mode-indent-offset 2)
(add-hook 'after-change-major-mode-hook
(defun on-go-ts-mode-hook ()
(when (derived-mode-p 'go-ts-mode 'go-mod-ts-mode)
(setq-local tab-width 2))))
(add-to-list 'auto-mode-alist `(,(rx ".go" eos) . go-ts-mode)))
Julia programming
Points for further investigation are:
- Application of the Julia Pkg documentation requires
(eval
or a terminal because the inferior Julia interpreter provides no genuine terminal. Type or clickBREAK
on theSIGNALS
menu to leave package mode. Type(eval
in package mode to see a list of added packages. Julia does not recognize dependencies anymore. Therefore, I have used package mode to addStaticLint
andSymbolServer
to make a patched version of eglot-jl work. - ESS support for the Julia language is a somewhat outdated description of how to obtain Julia support for Emacs Speaks Statistics and Emacs.
- Hands-On: Design Patterns and Best Practices with Julia is a Julia book with good reviews.
- Julia by example is a showcase of 1- or 2-liners.
- PkgTemplates and PkgTemplates User Guide explain how to write a Julia package. How to develop a Julia package is friendlier but not up to date.
- Practical Julia is Julia book with good reviews.
- The list of installed packages in listing lst:list-julia-packages-result facilitate Julia Code Blocks in Babel because the list contains CSV and DataFrames.
- How to update automatically to the current stable release of Julia.
A list of interesting online course material is:
- A Deep Introduction to Julia for Data Science and Scientific Computing.
- Julia for Optimization and Learning.
- Julia for Optimization and Learning Scripts.
- Quantitative Economics with Julia (use backspace to leave package mode).
Here are links to Julia programming background videos:
- Creating a Julia Pkg with PkgTemplates.jl
- Revise.jl, Docstrings, Tests, and structuring a Julia package
Listing lst:setup-julia setups Julia support but requires manual tweaking of Makefile to replace sed with gsed and of ess-autoloads.el.
(push (expand-file-name "~/VCS/ESS/lisp") load-path)
(when (require 'ess-julia nil 'noerror)
(setopt inferior-julia-program (executable-find "julia")))
cat "$HOME/.julia/environments/v1.10/Project.toml"
[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
Calculus = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
LanguageServer = "2b0e0bc5-e4fd-59b4-8912-456d1b03d8d7"
Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
PkgTemplates = "14b8a8f1-9102-5b29-a752-f990bacb7fe1"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
StaticLint = "b3cc710f-9c33-5bdb-a03d-a94903873e97"
SymbolServer = "cf896787-08d5-524d-9de7-132aaa0cb996"
cat "$HOME/VCS/ESS/0001-Patch-Makefile-for-Home-Brew.patch"
ESS Makefile for Homebrew patch
From 71729a02a190c1f1bfca02dd6db6d5f736437d5a Mon Sep 17 00:00:00 2001
From: Gerard Vermeulen <gerard.vermeulen@posteo.net>
Date: Mon, 26 Feb 2024 19:48:19 +0100
Subject: [PATCH] Patch Makefile for Home Brew
---
Makefile | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/Makefile b/Makefile
index 182094a3..3409cf66 100644
--- a/Makefile
+++ b/Makefile
@@ -4,14 +4,14 @@
include ./Makeconf
ESSVERSION := $(shell cat VERSION)
-PKGVERSION := $(shell sed -n 's/;; Version: *\(.*\) */\1/p' lisp/ess.el)
+PKGVERSION := $(shell gsed -n 's/;; Version: *\(.*\) */\1/p' lisp/ess.el)
ESSDIR := ess-$(ESSVERSION)
ifneq ($(ESSVERSION), $(PKGVERSION))
- $(shell sed -i 's/Version: .*/Version: $(ESSVERSION)/' VERSION)
- ${shell sed -i 's/;; Version: .*/;; Version: $(ESSVERSION)/' lisp/ess.el}
+ $(shell gsed -i 's/Version: .*/Version: $(ESSVERSION)/' VERSION)
+ ${shell gsed -i 's/;; Version: .*/;; Version: $(ESSVERSION)/' lisp/ess.el}
endif
-ESSR-VERSION := $(shell sed -n "s/;; ESSR-Version: *\(.*\) */\1/p" lisp/ess.el)
+ESSR-VERSION := $(shell gsed -n "s/;; ESSR-Version: *\(.*\) */\1/p" lisp/ess.el)
.PHONY: all
all: lisp doc etc autoloads
@@ -19,10 +19,10 @@ all: lisp doc etc autoloads
.PHONY: version
version: VERSION
@echo "********************* VERSIONS **************************"
- @echo $(shell $(EMACS) --version | sed -n 1p)
+ @echo $(shell $(EMACS) --version | gsed -n 1p)
@echo ESS $(ESSVERSION)
@echo ESSR $(ESSR-VERSION)
- @sed -i "s/\"VERSION\"/\"$(ESSVERSION)\"/" lisp/ess-custom.el
+ @gsed -i "s/\"VERSION\"/\"$(ESSVERSION)\"/" lisp/ess-custom.el
@echo "*********************************************************"
.PHONY: lisp
@@ -59,7 +59,7 @@ autoloads:
essr: VERSION
@echo "**********************************************************"
@echo "** Making ESSRv$(ESSR-VERSION) **"
- @sed -i "s/^ *VERSION <- .*/ VERSION <- \"$(ESSR-VERSION)\"/" etc/ESSR/R/.load.R
+ @gsed -i "s/^ *VERSION <- .*/ VERSION <- \"$(ESSR-VERSION)\"/" etc/ESSR/R/.load.R
cd etc/ESSR/; ./BUILDESSR; cd -
@git add etc/ESSR.rds lisp/ess.el etc/ESSR/R/.load.R
git commit -m"ESSR Version $(ESSR-VERSION)"
@@ -160,7 +160,7 @@ cleanup-rel:
@rm -f tarballs dist tag homepage upload rel
%.spec: %.spec.in VERSION
- sed 's/@@VERSION@@/$(ESSVERSION)/g' $< > $@
+ gsed 's/@@VERSION@@/$(ESSVERSION)/g' $< > $@
## --- RELEASE section ---
--
2.42.0
Python programming
The Python Programming in Emacs wiki page lists options to enhance Emacs's
built-in python-mode
. Here, the focus is on two Emacs packages and four Python
packages:
- Eglot - Emacs polyGLOT: a builtin LSP client since Emacs-29.1 and its author (who is a prolific Common Lisp and Emacs Lisp programmer) has also contributed to other parts of Emacs.
- Jedi is a static analysis tool for Python that is typically used in plugins for editors or integrated development environments. Jedi has a focus on autocompletion and object definition lookup functionality.
- Nowadays, Ruff is the fastest Python linter and a replacement for Flake8 with a variety of its plugins. Charlie Marsh explains why he started Ruff in the post Python tooling could be much faster.
- Python LSP Server is in my opinion the most complete Language Server Protocol implementation for Python and it handles Ruff thanks to the python-lsp-ruff plugin.
- Code-cells allows to edit IPython or Jupyter notebooks in Emacs with help from either Jupytext or Pandoc.
Here are links covering how to integrate Emacs, Python and a Python LSP server, before plunging into the configuration steps:
- Building Your Own Emacs IDE with LSP
- Doom Emacs and Language Servers
- Eglot based Emacs Python IDE
- Getting started with lsp-mode for Python
- Python & Emacs, Take 3
- Python with Emacs: py(v)env and lsp-mode
Here are links to Python programming background videos:
- Dataclasses: The Code Generator to End All Code Generators - Raymond Hettinger
- Modern Dictionaries - Raymond Hettinger
- Python's Class Development Kit - Raymond Hettinger
- The Big Leap of Python-3.13 - Lukasz Langa
- The Mental Game of Python - Raymond Hettinger
- Thinking about Concurrency - Raymond Hettinger
- Thinking outside the GIL with AsyncIO and MultiProcessing - John Reese
- What can't WebAssembly do? - Katie Bell
Python-mode
Listing setup Python mode selects a common Python interpreter in a virtual
environment for use in python-mode
and ob-python
. The pythonic, pyvenv, and
pyvenv packages allow to handle Python virtual environments within Emacs. The
pyenv package provides support to work with pyenv (eventually with
pyenv-virtualenv) to select between different python versions (eventually each
with different environments). In the end, all those packages do is to set
python-shell-virtualenv-root
(in case of pyenv and pythonic) or tweak the
environment variables and restart the relevant Python child processes (in case
of pyvenv). Therefore, I replace those packages with listing access pyenv and
select a Python interpreter in a virtual environment to set
python-shell-virtualenv-root
from within Emacs.
Install isort and pyflakes to enable adding, removing, sorting, and fixing import statements in Python-mode, see the end of the commentary src_emacs-lisp[:results silent]{(find-library "python")} section.
Listing kickoff pyproject.toml proposal facilitates dropping a pyproject.toml file into Python projects which anyhow should switch to using a pyproject.toml file as explained in the post This Way Up: A Bottom-Up Look At Python Packaging. The packaging Python projects tutorial and the Python sample project walk you through the process of writing a pyproject.toml file. Ruff docstring setup explains how to setup documentation string checking in the pyproject.toml file.
Listing kickoff setup.cfg proposal implements the using black with other tools rules which make flake8 or pycodestyle agree with black's uncompromising style.
Finally, listing flake8-nocolor and ruff-nocolor pipe the stdout
output of the
flake8 and ruff executables through cat
to remove escape sequences.
(with-eval-after-load 'ob-python
(setopt org-babel-python-command (concat (or (executable-find "python3")
(executable-find "python"))
" -E")))
(with-eval-after-load 'python
(setopt python-indent-guess-indent-offset nil
python-shell-completion-native-disabled-interpreters '("ipython3"
"pypy")
python-shell-interpreter (or (executable-find "python3")
(executable-find "python"))
python-shell-interpreter-args "-E -i"
python-check-command (executable-find "ruff-nocolor")
python-flymake-command (list (executable-find "ruff-nocolor")
"--stdin-filename" "stdin" "-")))
(when (executable-find "pyenv")
(defun pyenv-full-path (version)
"Return the full path for VERSION."
(unless (string= version "system")
(concat (pyenv-root) (file-name-as-directory "versions") version)))
(defun pyenv-root ()
"Return \"pyenv root\" as a directory."
(cl-destructuring-bind (exit-code output)
(shell-command-with-exit-code "pyenv" "root")
(if (= 0 exit-code)
(file-name-as-directory (string-trim output))
(error "%s" (string-trim output)))))
(defun pyenv-version-name ()
"Return \"pyenv version-name\"."
(cl-destructuring-bind (exit-code output)
(shell-command-with-exit-code "pyenv" "version-name")
(if (= 0 exit-code)
(string-trim output)
(error "%s" (string-trim output)))))
(defun pyenv-versions ()
"Return \"pyenv versions --bare --skip-aliases\" as a list.
Complete the result with \"system\"."
(cl-destructuring-bind (exit-code output)
(shell-command-with-exit-code
"pyenv" "versions" "--bare" "--skip-aliases")
(if (= 0 exit-code)
(cons "system" (split-string output))
(error "%s" (string-trim output)))))
(defun pyenv-virtualenvs ()
"Return \"pyenv virtualenvs --bare --skip-aliases\" as a list."
(cl-destructuring-bind (exit-code output)
(shell-command-with-exit-code
"pyenv" "virtualenvs" "--bare" "--skip-aliases")
(if (= 0 exit-code)
(split-string output)
(error "%s" (string-trim output))))))
(with-eval-after-load 'python
(when (cl-every #'fboundp '(pyenv-full-path
pyenv-version-name
pyenv-versions
pyenv-virtualenvs))
(setq python-shell-virtualenv-root
(pyenv-full-path (or (pyenv-version-name)
(car (pyenv-virtualenvs))
(car (pyenv-versions)))))
(message "Now `python-shell-virtualenv-root' equals %S"
python-shell-virtualenv-root)
(defun set-python-shell-virtualenv-root-to-pyenv-version ()
"Set `python-shell-virtual-env-root' to a pyenv version."
(interactive)
(let* ((version-name (pyenv-version-name))
(prompt (format "pyenv version (%s): " version-name))
(choices (pyenv-versions))
(version (completing-read prompt choices nil 'require-match)))
(unless (string= version-name version)
(setq python-shell-virtualenv-root (pyenv-full-path version))
(setenv "PYENV_VERSION" version))
(message "Now `python-shell-virtualenv-root' equals %S"
python-shell-virtualenv-root)))
(defun set-python-shell-virtualenv-root-to-pyenv-virtualenv ()
"Set `python-shell-virtual-env-root' to a pyenv virtualenv."
(interactive)
(let* ((version-name (pyenv-version-name))
(prompt (format "pyenv virtualenv (%s): " version-name))
(choices (pyenv-virtualenvs))
(version (completing-read prompt choices nil 'require-match)))
(unless (string= version-name version)
(setq python-shell-virtualenv-root (pyenv-full-path version))
(setenv "PYENV_VERSION" version))
(message "Now `python-shell-virtualenv-root' equals %S"
python-shell-virtualenv-root)))))
# See:
# https://docs.astral.sh/ruff/configuration/
# https://pycqa.github.io/isort/docs/configuration/black_compatibility.html
# https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
[tool.black]
line-length = 88
[tool.isort]
profile = "black"
[tool.ruff]
line-length = 88
select = [
"ARG", # flake8-unused-arguments
"B", # flake8-bugbear
"C", # mccabe
"C4", # flake8-comprehensions
"E", # pycodestyle
"D", # pydocstyle
"F", # pyflakes
"UP", # pyupgrade
"W", # pycodestyle
]
ignore = [
"B905", # `zip()` without an explicit `strict=` parameter
"D202", # no blank lines allowed after function docstring
]
[tool.ruff.mccabe]
max-complexity = 15
[tool.ruff.pydocstyle]
convention = "numpy"
# Local Variables:
# mode: conf-toml-mode
# End:
[flake8]
docstring-convention = numpy
extend-select = B,F,W
extend-ignore = W503
max-complexity = 15
max-line-length = 88
[pycodestyle]
ignore = W503
max-line-length = 88
# Local Variables:
# mode: conf-toml
# End:
#!/bin/sh
flake8 "$@" | cat
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# sh-basic-offset: 2
# End:
#!/bin/sh
ruff "$@" | cat
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# sh-basic-offset: 2
# End:
Org Babel Ackermann Python test
# https://rosettacode.org/wiki/Ackermann_function#Python
# https://www.youtube.com/watch?v=i7sm9dzFtEI
def ack(M, N):
if M == 0:
return N + 1
elif N == 0:
return ack(M - 1, 1)
else:
return ack(M - 1, ack(M, N - 1))
ack(3, 3)
61
Package Installer for Python
Listing lst:shell-pip-list-outdated and lst:shell-pip-index-versions are two
examples of invoking pip in org-mode
shell
source blocks with the respective
results in listing lst:shell-pip-list-outdated-results and
lst:shell-pip-index-versions-results.
pip list --outdated
# WARNING: "pip index" is currently an experimental command.
pip index versions circular-buffer --no-color
circular-buffer (0.2.0)
Available versions: 0.2.0, 0.1.1, 0.1.0
(defgroup pip nil
"Client for accessing the \"Package Installer for Python\" and \"PyPI\"."
:group 'applications)
(defcustom pip-frozen-packages nil
"Frozen Python packages."
:group 'pip
:type '(repeat string))
;; Updating "docutils" may require a compatible "Sphinx" release.
;; (setopt pip-frozen-packages '("docutils"))
(defvar pip-outdated-packages nil
"Outdated Python packages.")
(defun pip--list-outdated-sentinel (process _event)
"Sentinel function for when the `pip-list-outdated' PROCESS succeeds."
(when (and (eq (process-status process) 'exit)
(zerop (process-exit-status process))
(buffer-live-p (process-buffer process)))
(with-current-buffer (process-buffer process)
(goto-char (point-min))
(setq pip-outdated-packages
(json-parse-buffer :array-type 'list :object-type 'alist))
(let ((alists pip-outdated-packages))
(while alists
(setcdr (assq 'name (car alists))
(string-replace "_" "-" (alist-get 'name (car alists))))
(setq alists (cdr alists))))
(kill-buffer)
(describe-variable 'pip-outdated-packages)
(message "Calling `%S' succeeded" #'pip-list-outdated))))
(defun pip-list-outdated ()
"Save the outdated Python packages in `pip-outdated-packages'.
This invokes an asynchronous process and finishes with a message."
(interactive)
(let ((command '("pip" "list" "--outdated" "--format" "json")))
(make-process
:name "pip-list-outdated"
:buffer (generate-new-buffer-name "*pip-list-outdated-output*")
:command command
:sentinel #'pip--list-outdated-sentinel)
(message "Running `%s' asynchronously" (string-join command " "))))
(defun pip--upgrade-maybe-sentinel (process _event)
"Sentinel function for when the `pip-upgrade-maybe' PROCESS succeeds."
(when (and (eq (process-status process) 'exit)
(buffer-live-p (process-buffer process)))
(display-buffer (process-buffer process))))
(defun pip-upgrade-maybe ()
"Install the latest version of outdated packages unless they are frozen.
This invokes an asynchronous process and finishes with displaying the process
buffer to check whether upgrading has made the dependencies incompatible."
(interactive)
(let (found)
(dolist (alist pip-outdated-packages found)
(let ((name (alist-get 'name alist))
(latest_version (alist-get 'latest_version alist)))
(unless (member name pip-frozen-packages)
(push (format "%s==%s" name latest_version) found))))
(if (consp found)
(let ((command
`("pip" "install" "--progress-bar" "off" ,@(nreverse found))))
(make-process
:name "pip-upgrade-maybe"
:buffer (generate-new-buffer-name "*pip-upgrade-maybe*")
:command command
:sentinel #'pip--upgrade-maybe-sentinel)
(message "Running `%s' asynchronously" (string-join command " ")))
(message "`pip-upgrade-maybe' found no packages to install"))))
Eglot for python-mode
Listing lst:configure-eglot+pylsp-ruff configures eglot for Python using the python-lsp-server with the pylsp-ruff plugin for the easiest way to let python-lsp-server use Ruff. It is better to uninstall the Python packages autopep8, flake8, pydocstyle, pylint, and rope.
Listing lst:eglot-directory-variables-for-python shows a proper .dir-locals.el file in the root directory of any Python project to start eglot automatically according to the configuration in listing lst:eglot-maybe-ensure.
Type
(eval
to dump aJSON
representation of src_emacs-lisp[:results silent]{(describe-variable
'eglot-workspace-configuration)} for debugging. The comment in listing minimal
Eglot setup also shows how to decrease or to increase the verbosity of Python
LSP server log output for debugging.
(with-eval-after-load 'eglot
(setq-default
eglot-workspace-configuration
;; Enable the `:pylsp_ruff' plugin and ensure to disable the
;; `:flake8', `:mccabe', and `:pycodestye' plugins.
'(:pylsp (:plugins
(:pylsp_ruff
(:enabled t)
:flake8 ;; It is better to uninstall flake8
(:enabled :json-false)
:mccabe ;; It is better to uninstall mccabe
(:enabled :json-false)
:pycodestyle ;; It is better to uninstall pycodestyle
(:enabled :json-false)
:jedi
(:auto_import_modules ["numpy"])
:jedi_completion
(:cache_for ["astropy"]))))))
;; A .dir-locals.el file proposal in the root of any
;; Python project or Org-mode project tangling Python files
;; to launch eglot automatically.
((nil ;; nil, since Emacs-29.1 filters out irrelevant variable names.
. ((eglot-workspace-configuration
;; Enable the `:pylsp_ruff' plugin and ensure to disable the
;; `:flake8', `:mccabe', and `:pycodestye' plugins.
. (:pylsp (:plugins
(:pylsp_ruff
(:enabled t)
:flake8 ;; It is better to uninstall flake8
(:enabled :json-false)
:mccabe ;; It is better to uninstall mccabe
(:enabled :json-false)
:pycodestyle ;; It is better to uninstall pycodestyle
(:enabled :json-false)
:jedi
(:auto_import_modules ["numpy"])
:jedi_completion
(:cache_for ["astropy"]))))))))
command | key map | keys |
xref-find-definition | global-map | (eval |
xref-pop | global-map | (eval |
flymake-goto-next-error | eglot-mode-map | (eval |
flymake-goto-prev-error | eglot-mode-map | (eval |
eglot-rename | eglot-mode-map | (eval |
eldoc-doc-buffer | eglot-mode-map | (eval |
Jedi
Listing lst:example-py is a Python example to test whether jedi in combination with eglot works when coding for instance numpy universal functions.
import numpy
import astropy.units as apu
a = numpy.arange(0, 11)
a = numpy.linspace(0, 10, num=11)
a = numpy.arccos(a)
q = apu.Quantity(a, apu.meter)
print(q)
Code-cells
Code-cells allows to edit IPython or Jupyter notebooks in Emacs with help from either Jupytext or Pandoc to translate between the ipynb format and different plain text formats. Sofar, I have used code-cells to open ipynb notebooks with
(eval
in Emacs for viewing. Listing lst:configure-code-cells configurescode-cells
.
(when (ensure-package-installation 'code-cells)
(with-eval-after-load 'code-cells
(let ((map code-cells-mode-map))
(keymap-set map "M-p" #'code-cells-backward-cell)
(keymap-set map "M-n" #'code-cells-forward-cell)
(keymap-set map "C-c C-c" #'code-cells-eval))))
Libraries
Library dash.el (info)
The library dash.el (info) positions itself as a modern alternative for the list
processing API of cl. It is a requirement of important packages in this Emacs
setup (for instance citeproc, magit, and smartparens). Listing
lst:configure-dash enables fontification of dash
macros and makes
info-lookup-symbol
take into account the dash
macros.
(when (fboundp #'global-dash-fontify-mode)
(global-dash-fontify-mode))
(when (fboundp #'dash-register-info-lookup)
(with-eval-after-load 'info-look
(dash-register-info-lookup)))
Minor Modes (info)
Synchronal multiple-region editing
(when (ensure-package-installation 'iedit)
(require 'iedit nil 'noerror))
Unobtrusive whitespace trimming
(when (and (ensure-package-installation 'ws-butler)
(fboundp 'ws-butler-mode))
(setopt ws-butler-keep-whitespace-before-point nil)
(add-hook 'prog-mode-hook #'ws-butler-mode)
(add-hook 'text-mode-hook #'ws-butler-mode))
Structural editing
Structural editing keeps character pairs (for instance parentheses, curly and
square brackets as well as single and double quotes) balanced to leave code (for
instance Lisp and Python) and text (for instance LaTeX and Org) structure
intact. I use smartparens which offers a normal mode (smartparens-mode
) and a
strict mode (smartparens-strict-mode
). Although both modes insert character
pairs, the normal mode allows to delete one of the paired characters easily
while the strict mode does not. Therefore, the strict mode is more for code
editing since it never breaks programming language rules and the normal mode is
more for text editing where structure is a matter of convention instead of
programming language rules.
For instance, the strict mode in Python allows to delete entire lists, tuples,
or the arguments after the cursor (what Emacs calls point
) in a function call
without breaking the character pair balance. In order to repair a broken
character pair balance, insert a single character by prefixing it with "C-q"
bound to quoted-insert
.
The smartparens documentation targets experienced Emacs users. The following links show how to put the documentation to practical use:
- The smartparens cheatsheet demonstrates its usage visually.
- Omar Antolin's gist "my-smartparens-config.el" is the first place to look for how to tweak smartparens. However, the gist may be partially obsolete, since it is not part of his current Emacs configuration.
- How to enable smartparens in the minibuffer after eval-expression explains
how the machinery after the first and after later usages of
eval-expression
differ and discusses options how to handle those differences.
Listing lst:configure-smartparens aims to configure smartparens for Go, LaTeX, Lisp dialects, Org, and Python. Execute src_emacs-lisp[:results silent]{(sp-cheat-sheet)} for short documentation taking into account the overridden key bindings in listing lst:configure-smartparens. Table tab:smartparens-commands-and-bindings lists commands with key bindings taken in order from src_emacs-lisp[:results silent]{(sp-cheat-sheet)} that takes the overrides of listing lst:configure-smartparens into account.
Despite the provocative post "What if structural editing was a mistake?",
smartparens is one of my favorite packages. In particular, smartparens handles
matching pairs in Org-mode files with export and source blocks for multiple
formats and languages almost (exceptions are due to Search failed
errors
inside source blocks) correctly. Therefore, show-smartparens-mode highlights
matching pairs immediately in front or after point in such files correctly,
contrary to for instance rainbow-delimiters.
(when (and (ensure-package-installation 'smartparens)
;; Require `smartparens-config' instead of `smartparens' to
;; disable pairing of the quote character for lisp modes.
(require 'smartparens-config nil 'noerror))
(setopt sp-base-key-bindings 'sp
sp-override-key-bindings '(("C-(" . sp-backward-slurp-sexp)
("C-)" . sp-forward-slurp-sexp)
("C-M-(" . sp-backward-barf-sexp)
("C-M-)" . sp-forward-barf-sexp)))
(when (fboundp 'smartparens-mode)
(dolist (symbol '(conf-toml-mode-hook prog-mode-hook text-mode-hook))
(add-hook symbol #'smartparens-mode)))
(when (fboundp 'smartparens-strict-mode)
(dolist (symbol '(emacs-lisp-mode-hook
go-ts-mode-hook
ielm-mode-hook
inferior-python-mode-hook
lisp-data-mode-hook
lisp-mode-hook
python-mode-hook
sly-mrepl-mode-hook))
(add-hook symbol #'smartparens-strict-mode)))
(when (fboundp 'go-ts-mode)
;; Stolen from `smartparens-go':
(sp-with-modes 'go-ts-mode
(sp-local-pair "{" nil :post-handlers '(("||\n[i]" "RET")))
(sp-local-pair "/*" "*/" :post-handlers '(("| " "SPC")
("* ||\n[i]" "RET"))))
;; Go has no sexp suffices. This fixes slurping:
;; (|foo).bar -> (foo.bar)
(add-to-list 'sp-sexp-suffix (list #'go-ts-mode 'regexp "")))
;; https://xenodium.com/emacs-smartparens-auto-indent/index.html
(defun indent-between-pair (&rest _ignored)
(newline)
(indent-according-to-mode)
(forward-line -1)
(indent-according-to-mode))
(dolist (left '("(" "[" "{"))
(sp-local-pair 'prog-mode left
nil :post-handlers '((indent-between-pair "RET"))))
(show-smartparens-global-mode +1))
command | keys | status |
sp-forward-sexp | (eval |
|
sp-backward-sexp | (eval |
|
sp-next-sexp | (eval |
|
sp-prev-sexp | (eval |
|
sp-down-sexp | (eval |
shadowed |
sp-backward-down-sexp | (eval |
|
sp-beginning-of-sexp | (eval |
|
sp-end-of-sexp | (eval |
|
sp-up-sexp | (eval |
|
sp-backward-up-sexp | (eval |
|
sp-kill-sexp | (eval |
|
sp-copy-sexp | (eval |
|
sp-forward-slurp-sexp | (eval |
override |
sp-backward-slurp-sexp | override | |
sp-forward-barf-sexp | (eval |
override |
sp-backward-barf-sexp | override | |
sp-forward-symbol | (eval |
|
sp-backward-symbol | (eval |
|
sp-unwrap-sexp | (eval |
shadowed |
sp-backward-unwrap-sexp | (eval |
|
sp-splice-sexp | (eval |
|
sp-splice-killing-backward | (eval |
|
sp-splice-sexp-killing-forward | (eval |
shadowed |
sp-select-next-thing | (eval |
|
sp-select-next-thing-exchange | (eval |
|
sp-mark-sexp | (eval |
Electric operators
Listing lst:configure-electric-operator configures electric-operator-mode
to
add spaces around operators for compatibility with for instance the Black code
formatter for Python.
(when (and (ensure-package-installation 'electric-operator)
(fboundp 'electric-operator-mode))
(dolist (symbol '(c-mode-common-hook julia-mode-hook python-mode-hook))
(add-hook symbol #'electric-operator-mode)))
Smart snippets
(when (ensure-package-installation 'yasnippet)
;; Set `yas-alias-to-yas/prefix-p' before loading `yasnippet'.
(setopt yas-alias-to-yas/prefix-p nil)
(when (fboundp 'yas-minor-mode)
(dolist (symbol '(LaTeX-mode-hook
org-mode-hook
python-mode-hook
python-ts-mode-hook))
(add-hook symbol #'yas-minor-mode))))
Display (info)
Narrowing
Narrowing means focusing in on some portion of the buffer and widening means
focussing out on the whole buffer. This allows to concentrate temporarily on
for instance a particular function or paragraph by removing clutter. The "Do
What I Mean" narrow-or-widen-dwim function allows to toggle between narrowed and
widened buffer states. Here, the function narrow-or-widen-dwim
operates also
on any Org table, Org source block, Org block, or Org subtree.
(with-eval-after-load 'emacs
(declare-function org-at-table-p "org" (&optional table-type))
(defun org-narrow-to-table ()
"Narrow buffer to table at point."
(interactive)
(if (org-at-table-p)
(narrow-to-region (org-table-begin) (org-table-end))
(user-error "Not in a table")))
(defun narrow-or-widen-dwim (p)
"Widen a narrowed buffer, narrow \"Do What I Mean\" otherwise.
DWIM means: region, Org table, Org source block, Org block, Org
subtree, LaTeX environment, TeX group, or defun, whichever
applies first. Narrowing to org-src-block actually calls
`org-edit-src-code'.
With prefix P, don't widen, just narrow even if buffer is already
narrowed."
(declare (interactive-only t))
(interactive "P")
(cond ((and (buffer-narrowed-p) (not p))
(widen))
((and (bound-and-true-p org-src-mode) (not p))
(org-edit-src-exit))
((region-active-p)
(narrow-to-region (region-beginning) (region-end)))
((derived-mode-p 'org-mode)
(or (with-demoted-errors "DWIM: %S" (org-narrow-to-table) t)
(with-demoted-errors "DWIM: %S" (org-edit-src-code) t)
(with-demoted-errors "DWIM: %S" (org-narrow-to-block) t)
(org-narrow-to-subtree)))
((derived-mode-p 'latex-mode)
(LaTeX-narrow-to-environment))
((derived-mode-p 'tex-mode)
(TeX-narrow-to-group))
(t
(narrow-to-defun))))
(keymap-set ctl-x-map "n t" #'org-narrow-to-table)
(keymap-set ctl-x-map "C-n" #'narrow-or-widen-dwim))
Visualize color codes and names
Listing lst:enable-rainbow-mode enables rainbow-mode
to colorize color codes
and names in buffers for debugging.
(when (and (ensure-package-installation 'rainbow-mode)
(fboundp 'rainbow-mode))
(setopt rainbow-x-colors-major-mode-list '(c++-mode
c-mode
emacs-lisp-mode
inferior-emacs-lisp-mode
lisp-interaction-mode
org-mode
python-mode))
(rainbow-mode +1))
Flash the line around point for visual feedback
Listing lst:flash-line-around-point implements flashing of the line around point for visual feedback after a selection of commands that make it hard to track point movements visually.
;; Ensure loading `pulse' with the dynamic variables `pulse-delay' and
;; `pulse-iterations' before masking them lexically instead of after
;; to prevent the warning triggered by lazy loading.
(when (require 'pulse nil 'noerror)
;; https://karthinks.com/software/batteries-included-with-emacs/
;; https://github.com/karthink/.emacs.d/blob/master/init.el#L2077
(defun flash-line-around-point (&rest _)
"Flash the line around point."
(let ((pulse-iterations 16)
(pulse-delay 0.1))
(pulse-momentary-highlight-one-line (point))))
(dolist (command '(scroll-up-command
scroll-down-command
recenter-top-bottom
other-window))
(advice-add command :after #'flash-line-around-point)))
Applications
Elfeed: Emacs web feed reader
Listing lst:set-elfeed-options sets elfeed
options, binds the elfeed
command, and makes a minimal attempt to enable emms
.
(when (and (ensure-package-installation 'elfeed)
(fboundp 'elfeed))
(keymap-global-set "C-x w" #'elfeed)
(with-eval-after-load 'elfeed
(setopt
elfeed-feeds
'(("https://frame.work/fr/fr/blog.rss" framework)
("https://nullprogram.com/feed/" c-wellons)
("https://planet.emacslife.com/atom.xml" planet-emacs)
("https://protesilaos.com/codelog.xml" p-stavrou)
("https://sachachua.com/blog/category/emacs/feed" s-chua)
("https://updates.orgmode.org/feed/updates" org-updates)
("https://www.bof.nl/rss/" bof)
("https://www.laquadrature.net/fr/rss.xml" lqdn)
("https://www.lemonde.fr/blog/huet/feed/" sciences))))
(with-eval-after-load 'elfeed-show
(when (fboundp 'emms-all)
(emms-all))))
Emacs Multimedia System (info)
The link my emms and elfeed setup is a nice introduction to configuring and
using emms
with elfeed
. Listing lst:set-emms-options configures emms
for mpv
while eliminating use of mpd
.
(when (ensure-package-installation 'emms)
(with-eval-after-load 'emms
(emms-all)
;; Since `emms-all' may add `emms-player-mpd' to `emms-player-list',
;; get rid off `emms-player-mpd' after calling `emms-all'.
(setopt emms-player-list '(emms-player-mpv)))
(with-eval-after-load 'emms-mode-line
(setopt emms-mode-line-format ""))
(with-eval-after-load 'emms-player-mpd
(setopt emms-player-mpd-music-directory (expand-file-name "~/Music")
emms-player-mpd-server-name "localhost"
emms-player-mpd-server-port "6600"
emms-player-mpd-verbose t))
(with-eval-after-load 'emms-player-mpv
(setopt emms-player-mpv-ipc-method 'ipc-server
emms-player-mpv-update-metadata t)
;; Uncomment the next two lines in case of too many broken YouTube links:
(add-to-list 'emms-player-mpv-parameters "--ytdl-format=best" 'append)
(add-to-list 'emms-player-mpv-parameters "--config=no" 'append)
;; The two previous lines may make the setup less user friendly.
(add-to-list 'emms-player-mpv-parameters "--fullscreen" 'append))
(with-eval-after-load 'emms-playing-time
(setopt emms-playing-time-display-format " %s "))
(with-eval-after-load 'emms-playlist-mode
(setopt emms-playlist-mode-center-when-go t))
(with-eval-after-load 'emms-streams
(setopt emms-streams-file
(no-littering-expand-etc-file-name "emms/streams.emms"))))
Local variables linking to Latexmk save-compile-display-loop
Only the Org source file shows the local variables footer.