5834 lines
252 KiB
Org Mode
5834 lines
252 KiB
Org Mode
#+title: Emacs setup for use with LaTeX, Lisp, Org, and Python
|
||
#+author: Gerard Vermeulen
|
||
#+latex_class: article
|
||
#+latex_class_options: [11pt,a4paper,english,svgnames]
|
||
#+macro: kbd (eval (by-backend-kbd-org-macro $1))
|
||
#+options: ^:{} timestamp:nil
|
||
#+property: header-args:emacs-lisp :exports code :tangle init.el
|
||
#+startup: showeverything
|
||
#+begin_src latex :noweb yes :results raw
|
||
,#+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>>
|
||
#+end_src
|
||
|
||
* Copying
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:copying
|
||
:END:
|
||
|
||
This README contains my Emacs setup for use with LaTeX, Lisp, Org, and Python.
|
||
Copyright \copy 2021-2024 Gerard Vermeulen.
|
||
|
||
#+BEGIN_QUOTE
|
||
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/>.
|
||
#+END_QUOTE
|
||
|
||
#+BEGIN_QUOTE
|
||
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/>.
|
||
#+END_QUOTE
|
||
|
||
* Quick start
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:quick-start
|
||
:END:
|
||
|
||
This setup requires at least Emacs-30.0.60. The installation procedure starts
|
||
with:
|
||
1. Backup the =user-emacs-directory= which defaults often to =~/.emacs.d=.
|
||
2. Run the listing [[lst:prepare-user-emacs-directory-with-https][prepare the =user-emacs-directory= without =ssh= access]] or
|
||
the listing [[lst:prepare-user-emacs-directory-with-ssh][prepare the =user-emacs-directory= with =ssh= access]] commands.
|
||
After invoking Emacs interactively, Emacs will ask you to install a selected set
|
||
of packages. Quit Emacs and invoke Emacs again.
|
||
|
||
#+caption[Prepare the user-emacs-directory without =ssh= access]:
|
||
#+caption: Clone and initialize the user-emacs-directory without =ssh= access.
|
||
#+name: lst:prepare-user-emacs-directory-with-https
|
||
#+begin_src shell -n :eval never :tangle no
|
||
cd ~
|
||
git clone https://forge.chapril.org/gav451/emacs.d.git .emacs.d
|
||
make --directory=.emacs.d init
|
||
emacs &
|
||
#+end_src
|
||
|
||
#+caption[Prepare the =user-emacs-directory= with =ssh= access]:
|
||
#+caption: Clone and initialize the user-emacs-directory with =ssh= access.
|
||
#+name: lst:prepare-user-emacs-directory-with-ssh
|
||
#+begin_src shell -n :eval never :tangle no
|
||
cd ~
|
||
git clone ssh://gitea@forge.chapril.org:222/gav451/emacs.d.git .emacs.d
|
||
make --directory=.emacs.d init
|
||
emacs &
|
||
#+end_src
|
||
|
||
* Introduction
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:introduction
|
||
:END:
|
||
|
||
This Emacs setup aims to install automatically a minimal set of extension
|
||
packages that allows to handle my reports and presentations. The file format of
|
||
the reports is [[https://orgmode.org/][Org Mode]] plain text with [[https://www.python.org/][Python]] source code blocks and the file
|
||
format of the presentations is [[https://www.latex-project.org/][LaTeX]].
|
||
|
||
This [[info:org#Top][org]] file (more precisely the original [[info:org#Top][org]] source file of this file)
|
||
illustrates three methods in my work-flow:
|
||
1. How to tangle (or export) source blocks from [[info:org#Top][org]] files. This file contains
|
||
source blocks to produce the files =early-init.el=, =init.el=, =latexmkrc=, =org-store-link=, and =example.py= by tangling.
|
||
2. How to export [[info:org#Top][org]] files to other formats such as [[https://en.wikipedia.org/wiki/HTML][HTML]], [[https://www.latex-project.org/][LaTeX]], and [[https://en.wikipedia.org/wiki/PDF][PDF]].
|
||
3. How [[info:org#Hyperlinks][org hyperlinks (info)]] allow to link inside and outside [[info:org#Top][Org Mode]]: hover
|
||
over or click on the links to experiment.
|
||
|
||
The [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX - Aalborg University Center TeX]] extension package provides a
|
||
powerful [[https://en.wikipedia.org/wiki/Text-based_user_interface][Text-based User Interface (TUI)]] environment to edit the [[https://www.latex-project.org/][LaTeX]]
|
||
presentations.
|
||
|
||
The [[https://github.com/bdarcus/citar][Citar]] extension package provides quick filtering and selecting of
|
||
bibliographic entries, and the option to run different commands on those
|
||
selections. [[https://github.com/bdarcus/citar][Citar]] exploits the extension package [[https://github.com/minad/vertico][Vertico]]. The [[https://github.com/andras-simonyi/citeproc-el][CiteProc]]
|
||
extension package provides [[https://citationstyles.org/][CSL: citation style language]] processing capabilities
|
||
to [[https://github.com/bdarcus/citar][Citar]] and [[https://orgmode.org/][Org Mode]]. A curated repository of CSL styles is the [[https://github.com/citation-style-language/styles#readme][citation style
|
||
language repository]].
|
||
|
||
The [[https://github.com/vedang/pdf-tools][PDF-Tools]] extension package renders [[https://en.wikipedia.org/wiki/PDF][PDF]] file with the possibility to
|
||
annotate the file or to click on anchors in the [[https://en.wikipedia.org/wiki/PDF][PDF]] file that link back to the
|
||
original [[https://www.latex-project.org/][LaTeX]] file of the document. An example of my work-flow are the steps
|
||
to convert this [[info:org#Top][org]] file to [[https://en.wikipedia.org/wiki/PDF][PDF]] and to see the result with [[HTTPS://github.com/vedang/pdf-tools][PDF-Tools]] in Emacs:
|
||
execute the commands ~pdf-tools-install~, ~org-babel-tangle~,
|
||
~org-latex-export-latex-to-latex~, and ~compile~. This sets up an infinite
|
||
[[https://www.latex-project.org/][LaTeX]] compilation loop to update and redisplay the [[https://en.wikipedia.org/wiki/PDF][PDF]] file after execution of
|
||
the ~org-latex-export-latex-to-latex~ command in this buffer.
|
||
|
||
Here follows a list of links on how to use Emacs and Elisp:
|
||
1. [[https://www.masteringemacs.org/][Mastering Emacs]] is a link to a blog with interesting posts that promotes a
|
||
book on how to become a proficient Emacs user.
|
||
2. [[https://www2.lib.uchicago.edu/keith/emacs/][Use GNU Emacs: The Plain Text Computing Environment]] explains the fundamentals
|
||
of the Emacs abstractions before showing how to exploit those abstractions
|
||
interactively. It targets a similar audience as the [[https://www.masteringemacs.org/][Mastering Emacs]] book.
|
||
3. [[https://www.youtube.com/watch?v=lkIicfzPBys][A guided tour of Emacs]] is a link to a video tour pointing how [[info:emacs#Buffers][buffers (info)]],
|
||
[[info:emacs#Dired][dired (info)]], [[info:emacs#Help][documentation (info)]], [[info:elisp#Top][elisp (info)]], [[info:elisp#Debugging][elisp debugging (info)]],
|
||
[[info:eshell#Top][eshell (info)]], and [[info:emacs#Keyboard Macros][keyboard macros (info)]] turn Emacs in an powerful coding
|
||
and editing environment.
|
||
4. [[https://www.youtube.com/watch?v=6ZWp05OW1c0][Emergency Emacs]] is a link to a video on fundamental editing features and
|
||
principles with focus on [[info:ediff#Top][ediff (info)]], [[info:emacs#Undo][undo (info)]], [[info:emacs#Moving Point][moving point (info)]],
|
||
[[info:emacs#Erasing][erasing (info)]], and [[info:emacs#Dynamic Abbrevs][dynamic abbreviations (info)]].
|
||
5. [[https://endlessparentheses.com/][Endless Parentheses]] is a blog with mindblowing code snippets.
|
||
6. [[https://www.murilopereira.com/how-to-open-a-file-in-emacs/][How to open a file in Emacs]] looks into [[info:elisp#Top][elisp (info)]] and
|
||
[[info:elisp#Debugging][elisp debugging (info)]] and then compares Emacs, [[https://www.vim.org/][Vim]],
|
||
[[https://neovim.io/][Neovim]], and [[https://code.visualstudio.com/][Visual Studio Code]] with respect to values and technology.
|
||
7. [[https://protesilaos.com/codelog/2022-01-31-learning-emacs/][Learning Emacs and Elisp]] is a link to a video tutorial with a transcript on
|
||
the best approach to learn Emacs and Elisp.
|
||
|
||
* [[info:emacs#Early Init File][Early Init File (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:early-init-file
|
||
:END:
|
||
|
||
Try to load [[https://github.com/emacscollective/no-littering][no-littering]] as early as possible, since it helps to keep
|
||
=~/.emacs.d= clean.
|
||
|
||
#+caption[Tangle the early-init-file]:
|
||
#+caption: Tangle the early-init-file.
|
||
#+name: lst:tangle-early-init-file
|
||
#+begin_src emacs-lisp -n :results silent :tangle early-init.el
|
||
;;; early-init.el --- user early-init file -*- lexical-binding: t -*-
|
||
;;; Commentary:
|
||
;;; Code:
|
||
(setq load-prefer-newer t)
|
||
|
||
(require 'no-littering nil 'noerror)
|
||
;; Emacs looks for "Local variables:" after the last "form-feed".
|
||
|
||
;; Local Variables:
|
||
;; indent-tabs-mode: nil
|
||
;; End:
|
||
;;; early-init.el ends here
|
||
#+end_src
|
||
|
||
In order to get help in understanding the code block above in a buffer showing
|
||
the original [[info:org#Top][Org]] source file, type {{{kbd(C-c C-c)}}} after moving
|
||
point (or cursor) to one of the items of the list:
|
||
1. src_emacs-lisp[:results none]{(describe-variable 'load-prefer-newer)}
|
||
2. src_emacs-lisp[:results none]{(apropos-library "no-littering")}
|
||
3. src_emacs-lisp[:results none]{(info "(emacs) Pages")}
|
||
4. src_emacs-lisp[:results none]{(info "(emacs) Specifying File Variables")}
|
||
5. 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*. See also the [[https://github.com/emacsmirror/form-feed][form-feed]] extension
|
||
package which this setup does not install.
|
||
|
||
* [[info:emacs#Init File][Init File (info)]] header
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:init-file-header
|
||
:END:
|
||
|
||
The =user-init-file= header requires =cl-lib= and sets Emacs options. It
|
||
consists of three parts in listing [[lst:1st-setopt-call]], [[lst:2nd-setopt-call]], and
|
||
[[lst:2nd-setopt-call]] in order to limit the length of the listings for exporting
|
||
to LaTeX.
|
||
|
||
The [[info:emacs#Init File][init file (info)]] does load the ~custom-file~ according to the recommendation
|
||
of [[info:emacs#Saving Customizations][saving customizations (info)]].
|
||
|
||
#+caption[Set the first set of Emacs options]:
|
||
#+caption: Set the first set of Emacs options.
|
||
#+name: lst:1st-setopt-call
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;;; init.el --- user init file -*- lexical-binding: t -*-
|
||
;;; Commentary:
|
||
;;; Code:
|
||
|
||
(require 'cl-lib)
|
||
|
||
(when (version< emacs-version "30.0.60")
|
||
(error "This `init.el' requires at least Emacs-30.0.60"))
|
||
|
||
(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
|
||
indent-tabs-mode nil
|
||
inhibit-startup-buffer-menu t
|
||
inhibit-startup-screen 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 history-length
|
||
recentf-mode t
|
||
savehist-mode t
|
||
scroll-bar-mode nil
|
||
tab-always-indent 'complete
|
||
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)
|
||
#+end_src
|
||
|
||
#+caption[Set the second set of Emacs options: pin packages to archives]:
|
||
#+caption: Set the second set of Emacs options: pin packages to archives.
|
||
#+name: lst:2nd-setopt-call
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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")
|
||
(compat . "gnu")
|
||
(consult . "gnu")
|
||
(dash . "melpa-stable")
|
||
(debbugs . "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")
|
||
(realgud . "melpa")
|
||
(s . "melpa-stable")
|
||
(xr . "gnu")
|
||
(vertico . "gnu")
|
||
(with-editor . "nongnu")
|
||
(yasnippet . "gnu"))
|
||
package-selected-packages '(no-littering))
|
||
#+end_src
|
||
|
||
#+caption[Set the third set of Emacs options: final tweaks]:
|
||
#+caption: Set the third set of Emacs options: final tweaks.
|
||
#+name: lst:3rd-setopt-call
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; To use different Org git repositories (org or gro):
|
||
;; (push (expand-file-name "~/VCS/org-mode/lisp") load-path)
|
||
;; Postpone loading Org after shadowing Org and sh-script faces below.
|
||
|
||
(when (eq system-type 'darwin)
|
||
;; Failed to initialize color list unarchiver: ...
|
||
;; BUG#32854 and BUG#70836: rm ~/Library/Colors/Emacs.clr
|
||
;; INSTALL file: '--without-dbus --without-gconf --without-gsettings'.
|
||
(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))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Package Installation][Install the selected packages (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:install-selected-packages
|
||
:END:
|
||
|
||
[[info:emacs#Package Installation][Emacs installs packages]] from archives on the internet. This setup uses five
|
||
archives:
|
||
1. The [[https://elpa.gnu.org/][GNU Emacs Lisp Package Archive]].
|
||
2. The [[https://elpa.gnu.org/devel/index.html][Development Emacs Lisp Package Archive]].
|
||
3. The [[https://elpa.nongnu.org/][NonGNU Emacs Lisp Package Archive]].
|
||
4. The [[https://melpa.org/#/][Milkypostman's Emacs Lisp Package Archive (MELPA)]] with its official
|
||
[[https://www.mirrorservice.org/sites/melpa.org/packages/][mirror]] in case [[https://downforeveryoneorjustme.com/melpa.org][is Melpa.org down?]] tells [[https://melpa.org/#/][MELPA]] is down for everyone.
|
||
5. The [[https://stable.melpa.org/#/getting-started][Stable Milkypostman's Emacs Lisp Package Archive]].
|
||
|
||
The code in listing [[lst:install-selected-packages]] assumes that the package
|
||
system is in a *virgin* state if the package [[https://github.com/emacscollective/no-littering][no-littering]] is not present:
|
||
1. It installs and loads [[https://github.com/emacscollective/no-littering][no-littering]] after ensuring refreshing of the
|
||
contents of available packages.
|
||
2. It 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.
|
||
3. 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.
|
||
|
||
#+caption[Install the selected packages]:
|
||
#+caption: Install the selected packages.
|
||
#+name: lst:install-selected-packages
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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)
|
||
;; Install the selected packages.
|
||
(package-install-selected-packages)))
|
||
|
||
(defun ensure-package-selection (package)
|
||
"Ensure selection of PACKAGE."
|
||
(when (and (package-installed-p package)
|
||
(bound-and-true-p package-selected-packages))
|
||
(cl-pushnew package package-selected-packages)))
|
||
|
||
(defun ensure-package-installation (&rest packages)
|
||
"Ensure installation of all packages in PACKAGES."
|
||
(let ((ok t))
|
||
(dolist (package packages)
|
||
(unless (package-installed-p package)
|
||
(package-install package))
|
||
(unless (ensure-package-selection package)
|
||
(setq ok nil)))
|
||
ok))
|
||
#+end_src
|
||
|
||
* [[https://git.savannah.gnu.org/cgit/emacs.git/tree/admin/notes/tree-sitter/starter-guide?h=feature/tree-sitter][Emacs Tree-sitter]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-tree-sitter
|
||
:END:
|
||
|
||
Mickey Peterson's post [[https://www.masteringemacs.org/article/how-to-get-started-tree-sitter?utm_source=newsletter&utm_medium=rss][How To Get Started with Tree-Sitter]] explains how to use
|
||
=tree-sitter= in Emacs-29.1. Listing [[lst:setup-treesit-sources][Setup =treesit= grammar sources]] configures
|
||
where to find the different =tree-sitter= grammar sources for different
|
||
languages. Listing [[lst:setup-go][Setup Go programming]] shows how to configure ~tab-width~ for
|
||
~go-ts-mod~ and ~go-mode-ts-mode~.
|
||
|
||
#+caption[Setup =treesit= grammar sources]:
|
||
#+caption: Setup =treesit= grammar sources.
|
||
#+name: lst:setup-treesit-sources
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (require 'treesit nil 'noerror)
|
||
(setopt
|
||
treesit-language-source-alist
|
||
'((bash "https://github.com/tree-sitter/tree-sitter-bash" "master" "src")
|
||
(c "https://github.com/tree-sitter/tree-sitter-c" "master" "src")
|
||
(cmake "https://github.com/uyha/tree-sitter-cmake" "master" "src")
|
||
(cpp "https://github.com/tree-sitter/tree-sitter-cpp" "master" "src")
|
||
(css "https://github.com/tree-sitter/tree-sitter-css" "master" "src")
|
||
(glsl "https://github.com/theHamsta/tree-sitter-glsl" "master" "src")
|
||
;; go-ts-mode requires libtree-sitter-go and libtree-sitter-gomode.
|
||
(go "https://github.com/tree-sitter/tree-sitter-go" "master" "src")
|
||
(gomod "https://github.com/camdencheek/tree-sitter-go-mod" "main" "src")
|
||
(html "https://github.com/tree-sitter/tree-sitter-html" "master" "src")
|
||
(java "https://github.com/tree-sitter/tree-sitter-java" "master" "src")
|
||
(javascript "https://github.com/tree-sitter/tree-sitter-javascript"
|
||
"master" "src")
|
||
(json "https://github.com/tree-sitter/tree-sitter-json" "master" "src")
|
||
(julia "https://github.com/tree-sitter/tree-sitter-julia" "master" "src")
|
||
(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"))))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Faces][Text faces or styles (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:text-faces-or-styles
|
||
:END:
|
||
|
||
This setup does not use [[info:emacs#Custom Themes][custom themes (info)]], but it does a minimal setup of
|
||
fonts and faces. The [[https://protesilaos.com/codelog/2020-09-05-emacs-note-mixed-font-heights/][note on mixed font heights in Emacs]] for how to setup fonts
|
||
properly. It boils down to two rules:
|
||
1. The height of the default face must be an integer number to make the height a
|
||
physical quantity.
|
||
2. The heights of all other faces must be real numbers to scale those heights
|
||
with respect to the height of the face (those heights default to 1.0 for no
|
||
scaling).
|
||
The code in listing [[lst:configure-face-attributes]] 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{(set-default-face-height)}. To adjust the
|
||
font size in the current buffer, type {{{kbd(s-=)}}} or {{{kbd(s-+)}}} 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:
|
||
1. Toggling between a dark and light background by means of
|
||
src_emacs-lisp{(invert-default-face)}.
|
||
2. Shadowing the definition of faces before loading. The last item in the page
|
||
on [[https://orgmode.org/worg/org-contrib/babel/examples/fontify-src-code-blocks.html#org5c4406f][fontifying Org mode source code blocks]] describes this method.
|
||
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
|
||
{{{kbd(M-x describe-text-properties)}}} to inspect faces at point. Listing
|
||
[[lst:disable-mac-mouse-change-font-size]] tries to disable accidental changing of
|
||
the font size with the mouse on Darwin. Listing [[lst:setup-visual-line-mode]]
|
||
configures =visual-line-mode=.
|
||
|
||
#+caption[Configure =face-attributes=]:
|
||
#+caption: Configure =face-attributes=.
|
||
#+name: lst:configure-face-attributes
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
;; Set face attributes.
|
||
(cond
|
||
((eq system-type 'darwin)
|
||
(set-face-attribute 'default nil :family "Hack" :height 120)
|
||
(set-face-attribute 'fixed-pitch nil :family "Hack")
|
||
(set-face-attribute 'variable-pitch nil :family "FiraGo"))
|
||
((eq system-type 'gnu/linux)
|
||
(set-face-attribute 'default nil :family "Hack" :height 110)
|
||
(set-face-attribute 'fixed-pitch nil :family "Hack")
|
||
(set-face-attribute 'variable-pitch nil :family "FiraGo"))
|
||
(t
|
||
(set-face-attribute 'default nil :family "Hack" :height 110)
|
||
(set-face-attribute 'fixed-pitch nil :family "Hack")
|
||
(set-face-attribute 'variable-pitch nil :family "DejaVu Sans"))))
|
||
#+end_src
|
||
|
||
#+caption[Implement =face= setting functions]:
|
||
#+caption: Implement =face= setting functions.
|
||
#+name: lst:face-setting-functions
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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)))
|
||
#+end_src
|
||
|
||
#+caption[Shadow Org font-lock faces to improve the readability]:
|
||
#+caption: Shadow Org font-lock faces to improve the readability.
|
||
#+name: lst:shadow-org-font-lock-faces
|
||
#+begin_src emacs-lisp -n
|
||
;; 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)
|
||
#+end_src
|
||
|
||
#+caption[Shadow Emacs font-lock faces to improve the readability]:
|
||
#+caption: Shadow Emacs font-lock faces to improve the readability.
|
||
#+name: lst:shadow-emacs-font-lock-faces
|
||
#+begin_src emacs-lisp -n
|
||
;; 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)
|
||
#+end_src
|
||
|
||
#+caption[Use =buffer-face-mode= to set fixed or variable pitch face]:
|
||
#+caption: Use =buffer-face-mode= to set fixed or variable pitch face.
|
||
#+name: lst:use-buffer-face-mode-for-fixed-or-variable-pitch-face
|
||
#+begin_src emacs-lisp -n
|
||
;; 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)
|
||
#+end_src
|
||
|
||
#+caption[Disable changing font size with mouse on Darwin]:
|
||
#+caption: Disable changing font size with mouse on Darwin.
|
||
#+name: lst:disable-mac-mouse-change-font-size
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (eq system-type 'darwin)
|
||
;; https://lmno.lol/alvaro/hey-mouse-dont-mess-with-my-emacs-font-size
|
||
;; Hint: reset font size with "C-u 0 M-x text-scale-adjust".
|
||
;; GAV: how to explain undefined "C-<wheel-left>" and "C-<wheel-right>"?
|
||
(global-set-key (kbd "<pinch>") 'ignore)
|
||
(global-set-key (kbd "<C-wheel-up>") 'ignore)
|
||
(global-set-key (kbd "<C-wheel-down>") 'ignore))
|
||
#+end_src
|
||
|
||
#+caption[Setup ~visual-line-mode~]:
|
||
#+caption: Setup ~visual-line-mode~.
|
||
#+name: lst:setup-visual-line-mode
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (fboundp 'visual-wrap-prefix-mode)
|
||
(add-hook 'visual-line-mode-hook 'visual-wrap-prefix-mode))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Windows][Window management (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:window-management
|
||
:END:
|
||
|
||
Mickey Peterson's post [[https://www.masteringemacs.org/article/demystifying-emacs-window-manager][Demystifying Emacs's Window Manager]] invites to improve
|
||
window placement. Listing [[lst:1st-window-management]] and
|
||
[[lst:2nd-window-management]] implement a selection of his recommendations. See:
|
||
1. [[info:elisp#The Zen of Buffer Display][The Zen of Buffer Display (info)]]
|
||
2. [[https://e17i.github.io/articles-emacs-display-1/][Configuring the Emacs display system]]
|
||
for technical information.
|
||
|
||
#+caption[Window management functions and key bindings]:
|
||
#+caption: Window management functions and key bindings.
|
||
#+name: lst:1st-window-management
|
||
#+begin_src emacs-lisp -n
|
||
(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))
|
||
#+end_src
|
||
|
||
#+caption[Window management variables]:
|
||
#+caption: Window management variables.
|
||
#+name: lst:2nd-window-management
|
||
#+begin_src emacs-lisp -n :results none
|
||
;; (info "(elisp) Displaying Buffers")
|
||
;; (info "(emacs) Window Choice")
|
||
;; (describe-function 'display-buffer)
|
||
(with-eval-after-load 'emacs
|
||
;; https://www.masteringemacs.org/article/demystifying-emacs-window-manager
|
||
(setopt switch-to-buffer-obey-display-actions t)
|
||
(add-to-list 'display-buffer-alist
|
||
`(,(rx (or "*Apropos*" "*Dictionary*"))
|
||
(display-buffer-reuse-window display-buffer-pop-up-window)
|
||
(inhibit-same-window . nil)))
|
||
(add-to-list 'display-buffer-alist
|
||
`(,(rx (or "*Help*" "*info*"))
|
||
display-buffer-pop-up-window
|
||
(inhibit-same-window . t)))
|
||
(add-to-list 'display-buffer-alist
|
||
`(,(rx (or "*Occur*" "*grep*" "*xref*"))
|
||
display-buffer-reuse-window
|
||
(inhibit-same-window . nil)))
|
||
;; BUG#70773: Eli tells `(setq delayed-warnings-hook nil)' is better.
|
||
;; BUG#70795: Inconsistent `warning-display-at-bottom' behavior.
|
||
;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-05/msg00359.html
|
||
;; Works now always as 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))))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Bookmarks][Bookmarks (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:bookmarks
|
||
:END:
|
||
|
||
Listing [[lst:setup-bookmark][bookmark setup]] preserves bookmark history in [[./var/bookmark-default.el][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 [[info:consult#Top][consult (info)]]).
|
||
|
||
#+caption[Let Emacs save bookmarks when killed]:
|
||
#+caption: Let Emacs save bookmarks when killed.
|
||
#+name: lst:setup-bookmark
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'bookmark
|
||
(setopt bookmark-save-flag 1)
|
||
(setf bookmark-exit-hook 'bookmark-unload-function))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Bookmark commands with key bindings]:
|
||
#+caption: Bookmark commands with key bindings.
|
||
#+name: tab:bookmark-commands-and-bindings
|
||
|------------------------------+--------------------|
|
||
| command | key binding |
|
||
|------------------------------+--------------------|
|
||
| *bookmark-delete* | |
|
||
| *bookmark-delete-all* | |
|
||
| *bookmark-insert* | {{{kbd(C-x r i)}}} |
|
||
| *bookmark-insert-location* | |
|
||
| *bookmark-jump* | {{{kbd(C-x r j)}}} |
|
||
| *bookmark-jump-other-frame* | |
|
||
| *bookmark-jump-other-window* | |
|
||
| *bookmark-load* | |
|
||
| *bookmark-rename* | |
|
||
| *bookmark-save* | |
|
||
| *bookmark-set* | {{{kbd(C-x r m)}}} |
|
||
| *bookmark-set-no-overwrite* | {{{kbd(C-x r M)}}} |
|
||
| *bookmark-write* | |
|
||
| *consult-bookmark* | {{{kbd(C-x r b)}}} |
|
||
| *list-bookmarks* | |
|
||
|------------------------------+--------------------|
|
||
|
||
* [[info:emacs#Registers][Registers (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:registers
|
||
:END:
|
||
|
||
Listing [[lst:register-separator-usage-example][register separator usage example]] shows how to use a register
|
||
{{{kbd(+)}}} as register separator when appending regions to text registers.
|
||
Listing [[lst:setup-register][setup register usage]] prepares my preferred register usage. Table
|
||
[[tab:register-commands-and-bindings]] lists all =register= commands with working
|
||
key bindings. The =desktop= library allows to preserve the =register= history
|
||
between Emacs sessions.
|
||
|
||
#+caption[Register separator usage example]:
|
||
#+caption: Register separator usage example.
|
||
#+name: lst:register-separator-usage-example
|
||
#+begin_src text -n :exports code
|
||
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
|
||
#+end_src
|
||
|
||
#+caption[Setup register usage]:
|
||
#+caption: Setup register usage.
|
||
#+name: lst:setup-register
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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)))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Register commands with key bindings]:
|
||
#+caption: Register commands with key bindings.
|
||
#+name: tab:register-commands-and-bindings
|
||
|------------------------------------+------------------------|
|
||
| command | key binding |
|
||
|------------------------------------+------------------------|
|
||
| *append-to-register* | |
|
||
| *copy-rectangle-to-register* | {{{kbd(C-x r r)}}} |
|
||
| *copy-to-register* | {{{kbd(C-x r s)}}} |
|
||
| *frameset-to-register* | {{{kbd(C-x r f)}}} |
|
||
| *increment-register* | {{{kbd(C-x r +)}}} |
|
||
| *insert-register* | {{{kbd(C-x r i)}}} |
|
||
| *jump-to-register* | {{{kbd(C-x r j)}}} |
|
||
| *kmacro-to-register* | {{{kbd(C-x C-k x)}}} |
|
||
| *number-to-register* | {{{kbd(C-x r n)}}} |
|
||
| *point-to-register* | {{{kbd(C-x r <SPC>)}}} |
|
||
| *prepend-to-register* | |
|
||
| *set-register* | |
|
||
| *window-configuration-to-register* | {{{kbd(C-x r w)}}} |
|
||
|------------------------------------+------------------------|
|
||
|
||
* [[info:emacs#Dired][Dired: directory editor as file manager (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:file-manager
|
||
:END:
|
||
|
||
[[info:emacs#Dired][Dired (info)]] offer capabilities for copying, deleting, opening, renaming, and
|
||
viewing files and directories. 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 [[info:emacs#Dired][dired (info)]] as follows:
|
||
1. Type {{{kbd(C-x d)}}} to open the directory containing the specific image
|
||
file in a =dired-mode= buffer.
|
||
2. Start searching for the specific image by navigating to the name of any image
|
||
file.
|
||
3. Type {{{kbd(v)}}} to switch to view the file in an =image-mode= buffer.
|
||
4. In the =image-mode= buffer, continue searching for the specific image by
|
||
typing:
|
||
- {{{kbd(n)}}} to view the next image in the =dired-mode= buffer,
|
||
- {{{kbd(p)}}} to view the previous image in the =dired-mode= buffer, and
|
||
- {{{kbd(q)}}} to quit the =image-mode= buffer and to go back to the =dired-mode= buffer.
|
||
5. After finding the specific image, use {{{kbd(C-c l)}}} in the =image-mode=
|
||
buffer or the =dired-mode= buffer to store the =org-link=.
|
||
6. Switch to the =org-mode= buffer and use {{{kbd(C-c C-l)}}} to insert the =org-link= into the buffer.
|
||
|
||
Listing [[lst:setup-dired]] makes the directory editor group the directories in
|
||
front of the files and sort each group alphabetically. It also adds a key
|
||
binding to =dired-mode-map= to open files with the [[https://en.wikipedia.org/wiki/Eww_(web_browser)][Emacs Web Wowser]]. Listing
|
||
[[lst:wrap-dired]] wraps =dired= to sort the directory entries in different ways.
|
||
|
||
#+caption[Setup =dired=]:
|
||
#+caption: Setup =dired=.
|
||
#+name: lst:setup-dired
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'dired
|
||
(setopt dired-dwim-target t
|
||
;; | switch | action |
|
||
;; |--------+----------------------------------------|
|
||
;; | -a | also list hidden entries |
|
||
;; | -l | use a long listing format |
|
||
;; | -G | skip long listing format group names |
|
||
dired-listing-switches "-alG --group-directories-first"
|
||
dired-recursive-copies 'always
|
||
dired-recursive-deletes 'always)
|
||
|
||
(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))
|
||
|
||
(with-eval-after-load 'wdired
|
||
(setopt wdired-allow-to-change-permissions t))
|
||
#+end_src
|
||
|
||
#+caption[Wrap =dired=]:
|
||
#+caption: Wrap =dired=.
|
||
#+name: lst:wrap-dired
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(defun dired-by-newest ()
|
||
"Wrap dired to sort directory entries by newest first."
|
||
(interactive)
|
||
(when-let ((dir (read-directory-name "Dired-by-newest (directory): ")))
|
||
(dired dir "-alt")))
|
||
|
||
(defun dired-by-oldest ()
|
||
"Wrap dired to sort directory entries by oldest first."
|
||
(interactive)
|
||
(when-let ((dir (read-directory-name "Dired-by-oldest (directory): ")))
|
||
(dired dir "-altr")))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Minibuffer][Minibuffer (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:minibuffer-completion-styles
|
||
:END:
|
||
|
||
Listing [[lst:touch-minibuffer-look]] implements sets minibuffer options and enables
|
||
=marginalia-mode= (after ensuring the installation of =marginalia=) which adds
|
||
extra information (for instance documentation strings to commands) to items in
|
||
the minibuffer. See [[info:emacs#Completion Styles][minibuffer completion styles (info)]] and [[https://www.masteringemacs.org/article/understanding-minibuffer-completion][Understanding
|
||
minibuffer completion]] for more information.
|
||
|
||
#+caption[Touch the =minibuffer= look]:
|
||
#+caption: Touch the =minibuffer= look.
|
||
#+name: lst:touch-minibuffer-look
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'minibuffer
|
||
;; https://www.masteringemacs.org/article/understanding-minibuffer-completion
|
||
(setopt
|
||
completion-category-overrides '((file (styles basic substring)))
|
||
completion-styles '(basic flex partial-completion substring)))
|
||
|
||
(when (ensure-package-installation 'marginalia)
|
||
(marginalia-mode +1))
|
||
#+end_src
|
||
|
||
* [[info:elisp#Processes][Processes (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:processes
|
||
:END:
|
||
|
||
Listing [[lst:process-utilities]] defines a function to run a (command-line) program
|
||
with arguments and to obtain a =cons= of its numeric return code as well as its
|
||
output to =stdout=.
|
||
|
||
#+caption[Process utilities]:
|
||
#+caption: Process utilities.
|
||
#+name: lst:process-utilities
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; https://gitlab.com/howardabrams/spacemacs.d/blob/master/layers/ha-org/funcs.el#L418
|
||
(defun get-program-code-output (program &rest args)
|
||
"Run PROGRAM with ARGS and return the `cons' of return-code and output."
|
||
(with-temp-buffer
|
||
(cons (apply 'call-process program nil (current-buffer) nil args)
|
||
(buffer-substring-no-properties (point-min) (point-max)))))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Help][Help (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:help
|
||
:END:
|
||
|
||
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 {{{kbd(C-h o)}}} instead of {{{kbd(C-h f)}}} or {{{kbd(C-h v)}}}.
|
||
|
||
#+caption[Setup =help=]:
|
||
#+caption: Setup =help=.
|
||
#+name: lst:setup-help
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'help-fns
|
||
;; ChatGPT recommends to require `shortdoc' contrary to the
|
||
;; `shortdoc-help-fns-examples-function' documentation string.
|
||
;; BUG#71537: "emacs -Q" works with a simpler `add-hook' form. Note:
|
||
;; `add-hook' should append `shortdoc-help-fns-examples-function' to
|
||
;; `help-fns-describe-function-functions'.
|
||
(add-hook 'help-fns-describe-function-functions
|
||
#'shortdoc-help-fns-examples-function 'append)
|
||
(setopt help-enable-symbol-autoload t))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Help key bindings]:
|
||
#+caption: Help key bindings.
|
||
#+name: tab:help-key-bindings
|
||
|--------------------------------+----------+------------------|
|
||
| command | key map | keys |
|
||
|--------------------------------+----------+------------------|
|
||
| *Info-goto-emacs-command-node* | help-map | {{{kbd(C-h F)}}} |
|
||
| *describe-function* | help-map | {{{kbd(C-h f)}}} |
|
||
| *describe-key* | help-map | {{{kbd(C-h k)}}} |
|
||
| *describe-symbol* | help-map | {{{kbd(C-h o)}}} |
|
||
| *describe-variable* | help-map | {{{kbd(C-h v)}}} |
|
||
| *info* | help-map | {{{kbd(C-h i)}}} |
|
||
|--------------------------------+----------+------------------|
|
||
|
||
** [[info:emacs#Name Help][Shortdoc-display-group (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:shortdoc-display-group
|
||
:END:
|
||
|
||
Listing [[lst:setup-shortdoc]] binds {{{kbd(C-h y)}}} to =shortdoc-display-group=.
|
||
|
||
#+caption[Setup =shortdoc=]:
|
||
#+caption: Setup =shortdoc=.
|
||
#+name: lst:setup-shortdoc
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (fboundp 'shortdoc-display-group)
|
||
(keymap-set help-map "y" #'shortdoc-display-group))
|
||
#+end_src
|
||
|
||
** [[info:emacs#Disabling][Disabling Commands (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:enable-disabled-commands
|
||
:END:
|
||
|
||
Execute src_emacs-lisp[:results none]{(find-library "novice")} to see how Emacs
|
||
prevents new users from shooting themselves in the feet. Listing
|
||
[[lst:configure-disabled-command-function]] enables [[https://www.emacswiki.org/emacs/DisabledCommands][disabled commands on the fly]].
|
||
|
||
#+caption[Configure the =disabled-command-function=]:
|
||
#+caption: Configure the =disabled-command-function=.
|
||
#+name: lst:configure-disabled-command-function
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
(setq disabled-command-function
|
||
(defun enable-this-command (&rest _args)
|
||
"Called when 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))))
|
||
#+end_src
|
||
|
||
** [[info:info#Top][Info (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:info
|
||
:END:
|
||
|
||
Listing [[lst:configure-info]] adds the required paths to the places where =info=
|
||
looks for files.
|
||
|
||
#+caption[Configure =info=]:
|
||
#+caption: Configure =info=.
|
||
#+name: lst:configure-info
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'info
|
||
;; Make Emacs find ALL "*.info" files in `package-user-dir' on Gentoo Linux.
|
||
(when (eq system-type 'gnu/linux)
|
||
(dolist (path
|
||
(nreverse
|
||
(mapcar
|
||
(lambda (name)
|
||
(expand-file-name (file-name-directory name)))
|
||
(directory-files-recursively package-user-dir "\\.info\\'"))))
|
||
(add-to-list 'Info-directory-list path nil #'file-equal-p)))
|
||
;; Make Emacs find the git Org info files.
|
||
;; (push (expand-file-name "~/VCS/org-mode/doc") Info-directory-list)
|
||
;; Make Emacs find Python info files.
|
||
(push (expand-file-name "~/.local/share/info") Info-directory-list))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Key Bindings][Key bindings (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:key-bindings
|
||
:END:
|
||
|
||
Table [[tab:basic-key-bindings]] lists basic key bindings. The =which-key= package
|
||
is builtin since Emacs-30.0.60 and enabling =which-key-mode= facilitates to
|
||
associate [[info:elisp#Key Sequences][key sequences (info)]] with commands:
|
||
1. This setup does not enable =which-key-mode= because of rare interference with
|
||
for instance a process that the =compile= command launches.
|
||
2. ChatGPT bullshits on "How to page in =which-key-mode= on Emacs". Anyhow, it
|
||
is easier to bypass paging.
|
||
3. The *easiest* solution is to bypass paging in =which-key-mode= as listing
|
||
[[lst:setup-which-key]] implements.
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Basic key bindings]:
|
||
#+caption: Basic key bindings.
|
||
#+name: tab:basic-key-bindings
|
||
|----------------------+------------+------------------------|
|
||
| command | key map | keys |
|
||
|----------------------+------------+------------------------|
|
||
| *undo* | global-map | {{{kbd(s-z)}}} |
|
||
| *backward-kill-word* | global-map | {{{kbd(C-backspace)}}} |
|
||
| *backward-char* | global-map | {{{kbd(C-b)}}} |
|
||
| *forward-char* | global-map | {{{kbd(C-f)}}} |
|
||
| *backward-word* | global-map | {{{kbd(M-b)}}} |
|
||
| *forward-word* | global-map | {{{kbd(M-f)}}} |
|
||
| *backward-sentence* | global-map | {{{kbd(M-a)}}} |
|
||
| *forward-sentence* | global-map | {{{kbd(M-e)}}} |
|
||
|----------------------+------------+------------------------|
|
||
|
||
#+caption[Setup =which-key=]:
|
||
#+caption: Setup =which-key=.
|
||
#+name: lst:setup-which-key
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; GAV: Bypass paging in `which-key-mode'.
|
||
(setopt max-mini-window-height 20)
|
||
(which-key-setup-minibuffer)
|
||
#+end_src
|
||
|
||
** [[https://github.com/michael-heerdegen/interaction-log.el#readme][Interaction-log]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:interaction-log
|
||
:END:
|
||
|
||
#+caption[Configure =interaction-log=]:
|
||
#+caption: Configure =interaction-log=.
|
||
#+name: lst:configure-interaction-log
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (and (ensure-package-installation 'interaction-log)
|
||
(require 'interaction-log nil 'noerror))
|
||
|
||
;; https://www.skybert.net/emacs/show-shortcuts-when-giving-presentations/
|
||
(defun ilog-ensure-ilog-buffer-window ()
|
||
"Ensure the visibility of the `ilog' buffer, when it exists."
|
||
(when (and (get-buffer ilog-buffer-name)
|
||
(not (get-buffer-window ilog-buffer-name)))
|
||
(display-buffer (get-buffer ilog-buffer-name))))
|
||
|
||
(keymap-set help-map "C-l" #'interaction-log-mode)
|
||
(advice-add 'ilog-timer-function :after #'ilog-ensure-ilog-buffer-window))
|
||
#+end_src
|
||
|
||
|
||
** [[https://github.com/tarsius/keycast#readme][Keycast]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:keycast
|
||
:END:
|
||
|
||
Listing [[lst:setup-keycast]] sets up =keycast= so that =keycast-log-mode= uses a
|
||
buffer similar to what calling =ediff-setup-windows-plain= returns.
|
||
|
||
#+caption[Setup =keycast=]:
|
||
#+caption: setup =keycast=.
|
||
#+name: lst:setup-keycast
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; Make `keycast-log-update-buffer' use a buffer similar to what
|
||
;; calling `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))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Emacs Server][Using Emacs as a server (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:using-emacs-server
|
||
:END:
|
||
|
||
Emacs can act as a server that listens to a socket to share its state (for
|
||
instance buffers and command history) with other programs by means of a shell
|
||
command =emacsclient=. Section [[#sec:latexmk-save-compile-display-loop]] explains
|
||
how to use ~emacsclient~ to install an asynchronous (or background) loop of
|
||
saving a LaTeX file, compiling it, and redisplaying the output in Emacs.
|
||
|
||
#+caption[Start the Emacs server]:
|
||
#+caption: Start the Emacs server.
|
||
#+name: lst:start-emacs-server
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when window-system
|
||
(unless (or noninteractive (daemonp))
|
||
(add-hook 'after-init-hook #'server-start)))
|
||
#+end_src
|
||
|
||
** Latexmk save-compile-display loop
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:latexmk-save-compile-display-loop
|
||
:END:
|
||
|
||
The =latexmk= resource file in listing [[lst:latexmkrc]] shows how to use
|
||
=emacsclient= to (re)display the PDF file in Emacs after each successful
|
||
(re)compilation on condition that the local ~compile-command~ variable is
|
||
correct. The local ~compile-command~ variable in the [[#sec:local-variables][local variables]] section
|
||
(only visible in =org= files, but not in =html= and =pdf= files) shows how to
|
||
make use of the =latexmkrc= file.
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Tangle the =Latexmk= resource file]:
|
||
#+caption: Tangle the =Latexmk= resource file.
|
||
#+name: lst:latexmkrc
|
||
#+begin_src perl -n :tangle latexmkrc :comments no
|
||
# pdf creator
|
||
$pdf_mode = 4; # 4 means lualatex
|
||
# pdf previewer and update pdf previewer
|
||
$pdf_previewer = "emacsclient -e '(find-file-other-window %S)'";
|
||
$pdf_update_method = 4; # 4 runs $pdf_update_command to force the update
|
||
$pdf_update_command = "emacsclient -e '(with-current-buffer (find-buffer-visiting %S) (pdf-view-revert-buffer nil t))'";
|
||
# see for instance glossary.latexmkrc
|
||
add_cus_dep( 'acn', 'acr', 0, 'makeglossaries' );
|
||
add_cus_dep( 'glo', 'gls', 0, 'makeglossaries' );
|
||
# use ".=" to append to $clean_ext
|
||
$clean_ext .= " acr acn alg bbl dvi glo gls glg ist lol lst";
|
||
$clean_ext .= " nav run.xml snm synctex.gz";
|
||
sub makeglossaries {
|
||
my ($name, $path) = fileparse( $$Psource );
|
||
return system "makeglossaries -d '$path' '$name'";
|
||
}
|
||
# Emacs looks for "Local variables:" after the last "form-feed".
|
||
|
||
# Local Variables:
|
||
# mode: perl
|
||
# End:
|
||
#+end_src
|
||
|
||
** TODO Org-protocol handling with other browser on Darwin :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:org-protocol-darwin
|
||
:END:
|
||
|
||
1. [[http://rwx.io/posts/osx-uri-protocol-handler/][Patrick Goddi: macOS URI protocol handler]] and his [[https://github.com/fooqri/uri-handler][URI-Handler]].
|
||
2. [[https://www.hammerspoon.org/][Hammerspoon]]
|
||
3. [[https://vritser.github.io/posts/capture-anywhere/][Emacs Capture Anywhere]]
|
||
|
||
* [[info:emacs#Calendar/Diary][Calendar, Diary, and Time (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:time
|
||
:END:
|
||
|
||
#+caption[Set =world-clock= options]:
|
||
#+caption: Set =world-clock= options.
|
||
#+name: lst:set-world-clock-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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"))
|
||
#+end_src
|
||
|
||
* Tools to handle buffers and modes :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:buffer-mode-tools
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
|
||
#+caption[Tools to handle buffers and modes]:
|
||
#+caption: Tools to handle buffers and modes.
|
||
#+name: lst:buffer-mode-tools
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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))))
|
||
(sort (cl-remove-duplicates result))))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Saving Emacs Sessions][Saving Emacs sessions (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:desktop
|
||
:END:
|
||
|
||
Listing [[lst:desktop-setup][setup desktop]] makes Emacs save its state between closing its session to
|
||
opening the next session. This setup relies on =desktop-save-mode= in case of
|
||
overlapping functionality of =desktop-save-mode= and =savehist-mode=. Listing
|
||
[[lst:prune-file-name-history]] prunes non-existing files from =file-name-history=,
|
||
which is under Emacs control but neither =desktop-save-mode= nor
|
||
=savehist-mode=.
|
||
|
||
NOTE: The src_emacs-lisp{(describe-function 'savehist-mode)} documentation
|
||
explains why it is best to turn on =savehist-mode= in =user-init-file=.
|
||
|
||
BUG: Adding ~kill-ring~ to ~savehist-additional-variables~ does not save
|
||
~kill-ring~ from an Emacs session to the next session, contrary to the
|
||
src_emacs-lisp{(describe-variable 'savehist-additional-variables)}
|
||
documentation.
|
||
|
||
#+caption[Setup =desktop= to save Emacs sessions]:
|
||
#+caption: Setup =desktop= to save Emacs sessions.
|
||
#+name: lst:desktop-setup
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
;; GAV: I fail to implement the idea of ChatGPT on how to reload
|
||
;; `eww' pages automatically while restoring the desktop (idea is to
|
||
;; add save and restore handlers to `desktop-buffer-mode-handlers').
|
||
;; GAV: (setopt eww-restore-desktop t) fails and starts hesitantly.
|
||
;; GAV: I fail to code a function for setting the
|
||
;; `desktop-buffers-not-to-save-function' option.
|
||
(setopt desktop-buffers-not-to-save
|
||
(rx bos (or "*Apropos" "compilation*"))
|
||
desktop-modes-not-to-save
|
||
'(emacs-lisp-mode image-mode tags-table-mode))
|
||
|
||
(desktop-save-mode t))
|
||
#+end_src
|
||
|
||
#+caption[Prune non-existing files from =file-name-history=]:
|
||
#+caption: Prune non-existing files from =file-name-history=.
|
||
#+name: lst:prune-file-name-history
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(defun prune-file-name-history ()
|
||
"Prune non-existing files from `file-name-history'."
|
||
(interactive)
|
||
(let ((old (length file-name-history)) ok)
|
||
(dolist (name file-name-history)
|
||
(when (file-exists-p name)
|
||
(push name ok)))
|
||
(setq file-name-history (nreverse ok))
|
||
(message "Pruned `file-name-history' from `%S' to `%S' files"
|
||
old (length file-name-history))))
|
||
#+end_src
|
||
|
||
* Completion
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:completion
|
||
:END:
|
||
|
||
Completion YouTube links are [[https://cestlaz.github.io/post/using-emacs-80-vertico/][Using Vertico, Marginalia, Consult, and Embark]] and
|
||
[[yt:fnE0lXoe7Y0][Emacs Completion Explained]]. This setup installs three external Emacs packages:
|
||
1. [[info:vertico#Top][Vertico (info)]] for a performant and minimalistic vertical completion UI.
|
||
2. [[info:company#Top][Company (info)]] to complete anything anywhere in Emacs.
|
||
3. [[info:consult#Top][Consult (info)]] for useful search and navigation commands.
|
||
|
||
** [[info:vertico#Top][Vertico (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:vertico-configuration
|
||
:END:
|
||
|
||
See the [[yt:hPwDbx--Waw][Vertico extensions for Emacs]] video for an overview. Listing
|
||
[[lst:setup-vertico-mode]] sets up =vertico-mode= without using the
|
||
=vertico-directory= extension (less is better!). Listing
|
||
[[lst:prune-file-name-history]] allows to prune non-existing files from the file
|
||
name history.
|
||
|
||
NOTE: Play with =vertico-buffer-mode=, =vertico-flat-mode=, =vertico-grid-mode=,
|
||
=vertico-indexed-mode=, =vertico-mouse-mode= (not easy to use with my trackpad),
|
||
=vertico-reverse-mode=.
|
||
|
||
#+caption[Setup =vertico-mode=]:
|
||
#+caption: Setup =vertico-mode=.
|
||
#+name: lst:setup-vertico-mode
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'vertico)
|
||
;; GAV: Builtin `fido-mode' may fix `find-file' completion when
|
||
;; using `vertico-directory' functions which confuse `find-file'.
|
||
;; GAV: Builtin `fido-mode' has more down than up sides: ChatGPT
|
||
;; bullshits on enhancing `vertico-mode' completion.
|
||
(vertico-mode +1)
|
||
;; GAV: Darwin shadows the "C-<up>" and "C-<down>" key bindings.
|
||
(keymap-set vertico-map "C-(" #'vertico-previous-group)
|
||
(keymap-set vertico-map "C-)" #'vertical-next-group))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Vertico key map bindings]:
|
||
#+caption: Vertico key map bindings.
|
||
#+name: tab:vertico-keymap-bindings
|
||
|--------------------------+------------------------------------+-------------------|
|
||
| command | remap | keys |
|
||
|--------------------------+------------------------------------+-------------------|
|
||
| *vertico-exit* | *exit-minibuffer* | {{{kbd(C-j)}}} |
|
||
| *vertico-first* | *beginning-of-buffer* | {{{kbd(M-<)}}} |
|
||
| *vertico-first* | *minibuffer-beginning-of-buffer* | {{{kbd(M-<)}}} |
|
||
| *vertico-insert* | | {{{kbd(TAB)}}} |
|
||
| *vertico-last* | *end-of-buffer* | {{{kbd(M->)}}} |
|
||
| *vertico-next-group* | *forward-paragraph* | {{{kbd(C-))}}} |
|
||
| *vertico-next* | *next-line-or-history-element* | {{{kbd(<down>)}}} |
|
||
| *vertico-next* | *next-line* | {{{kbd(C-n)}}} |
|
||
| *vertico-previous-group* | *backward-paragraph* | {{{kbd(C-()}}} |
|
||
| *vertico-previous* | *previous-line-or-history-element* | {{{kbd(<up>)}}} |
|
||
| *vertico-previous* | *previous-line* | {{{kbd(C-p)}}} |
|
||
| *vertico-save* | *kill-ring-save* | {{{kbd(M-w)}}} |
|
||
| *vertico-scroll-down* | *scroll-down-command* | {{{kbd(M-v)}}} |
|
||
| *vertico-scroll-up* | *scroll-up-command* | {{{kbd(C-v)}}} |
|
||
|--------------------------+------------------------------------+-------------------|
|
||
|
||
** [[https://company-mode.github.io/][Company: a modular in-buffer completion framework for Emacs]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:company-setup
|
||
:END:
|
||
|
||
[[info:company#Top][Company (info)]] completes anything anywhere in Emacs. Listing [[lst:setup-company]]
|
||
sets it up after ensuring its installation.
|
||
|
||
#+caption[Setup =company=]:
|
||
#+caption: Setup =company=.
|
||
#+name: lst:setup-company
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'company)
|
||
(with-eval-after-load 'company
|
||
;; GAV: Set `company-backends' more restrictively than `company'.
|
||
(setopt company-backends
|
||
'(company-capf company-files
|
||
(company-dabbrev-code company-keywords)
|
||
company-dabbrev)))
|
||
;; GAV: `desktop' may enable `company-mode' for files it remembers.
|
||
(add-hook 'emacs-lisp-mode-hook #'company-mode)
|
||
(add-hook 'lisp-interaction-mode-hook #'company-mode)
|
||
(add-hook 'lisp-mode-hook #'company-mode)
|
||
(add-hook 'python-mode-hook #'company-mode) ; works with `eglot'.
|
||
(add-hook 'ielm-mode-hook #'company-mode)
|
||
(add-hook 'sly-mrepl-mode-hook #'company-mode))
|
||
#+end_src
|
||
|
||
** [[info:consult#Top][Consult (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:consult-setup
|
||
:END:
|
||
|
||
[[info:consult#Top][Consult (info)]] provides practical commands based on the Emacs minibuffer
|
||
completion function [[info:elisp#Minibuffer Completion][completing-read]]. Listing [[lst:bind-consult-commands][bind =consult= commands]] binds
|
||
=consult= commands to different key maps.
|
||
Consult usage tips are:
|
||
1. Add a buffer position to the mark ring by means of {{{kbd(C-SPACE C-SPACE)}}}
|
||
and jump to one of the marks in the ring by means of {{{kbd(M-x
|
||
consult-mark)}}} or {{{kbd(M-g m)}}}. See [[https://arialdomartini.github.io//emacs-mark-ring][Emacs: Mark Ring]].
|
||
2. {{{kbd(M-x consult-org-heading)}}} or {{{kbd(C-c C-h)}}} is an alternative to
|
||
{{{kbd(M-x org-goto)}}} or {{{kbd(C-c C-j)}}}.
|
||
|
||
#+caption[Bind =consult= commands]:
|
||
#+caption: Bind =consult= commands.
|
||
#+name: lst:bind-consult-commands
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'consult)
|
||
;; C-c bindings (current-global-map)
|
||
(keymap-global-set "C-c h" #'consult-history)
|
||
(keymap-global-set "C-c m" #'consult-mode-command)
|
||
;; C-x bindings (ctl-x-map)
|
||
(keymap-set ctl-x-map "M-:" #'consult-complex-command)
|
||
(keymap-set ctl-x-map "b" #'consult-buffer)
|
||
(keymap-set ctl-x-4-map "b" #'consult-buffer-other-window)
|
||
(keymap-set ctl-x-5-map "b" #'consult-buffer-other-frame)
|
||
(keymap-set ctl-x-r-map "x" #'consult-register)
|
||
(keymap-set ctl-x-r-map "b" #'consult-bookmark)
|
||
;; M-g bindings (goto-map)
|
||
(keymap-set goto-map "g" #'consult-goto-line)
|
||
(keymap-set goto-map "M-g" #'consult-goto-line)
|
||
(keymap-set goto-map "o" #'consult-outline)
|
||
(keymap-set goto-map "m" #'consult-mark)
|
||
(keymap-set goto-map "k" #'consult-global-mark)
|
||
(keymap-set goto-map "i" #'consult-imenu)
|
||
(keymap-set goto-map "e" #'consult-compile-error)
|
||
(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))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Configuration specific key bindings]:
|
||
#+caption: Configuration specific key-bindings.
|
||
#+name: tab:configuration-specific-key-bindings
|
||
|-------------------------------+----------------+--------------------|
|
||
| command | key map | keys |
|
||
|-------------------------------+----------------+--------------------|
|
||
| *consult-bookmark* | ctl-x-r-keymap | {{{kbd(C-x r b)}}} |
|
||
| *consult-buffer-other-frame* | ctl-x-5-keymap | {{{kbd(C-x 5 b)}}} |
|
||
| *consult-buffer-other-window* | ctl-x-4-keymap | {{{kbd(C-x 4 b)}}} |
|
||
| *consult-buffer* | ctl-x-keymap | {{{kbd(C-x b)}}} |
|
||
| *consult-compile-error* | goto-map | {{{kbd(M-g e)}}} |
|
||
| *consult-complex-command* | ctl-x-keymap | {{{kbd(C-x M-:)}}} |
|
||
| *consult-find* | search-map | {{{kbd(M-s f)}}} |
|
||
| *consult-focus-lines* | search-map | {{{kbd(M-s u)}}} |
|
||
| *consult-git-grep* | search-map | {{{kbd(M-s g)}}} |
|
||
| *consult-global-mark* | goto-map | {{{kbd(M-g k)}}} |
|
||
| *consult-goto-line* | goto-map | {{{kbd(M-g M-g)}}} |
|
||
| *consult-goto-line* | goto-map | {{{kbd(M-g g)}}} |
|
||
| *consult-history* | global-map | {{{kbd(C-c h)}}} |
|
||
| *consult-imenu* | goto-map | {{{kbd(M-g i)}}} |
|
||
| *consult-keep-lines* | search-map | {{{kbd(M-s k)}}} |
|
||
| *consult-line* | search-map | {{{kbd(M-s l)}}} |
|
||
| *consult-mark* | goto-map | {{{kbd(M-g m)}}} |
|
||
| *consult-mode-command* | global-map | {{{kbd(C-c m)}}} |
|
||
| *consult-multi-occur* | search-map | {{{kbd(M-s m)}}} |
|
||
| *consult-org-heading* | org-mode-map | {{{kbd(C-c C-h)}}} |
|
||
| *consult-outline* | goto-map | {{{kbd(M-g o)}}} |
|
||
| *consult-register* | ctl-x-r-keymap | {{{kbd(C-x r x)}}} |
|
||
| *consult-yank-pop* | global-map | {{{kbd(M-y)}}} |
|
||
|-------------------------------+----------------+--------------------|
|
||
| *elfeed* | global-map | {{{kbd(C-x w)}}} |
|
||
| *iedit-mode* | global-map | {{{kbd(C-;)}}} |
|
||
| *narrow-or-widen-dwim* | ctl-x-keymap | {{{kbd(C-x C-n)}}} |
|
||
| *org-agenda* | global-map | {{{kbd(C-c a)}}} |
|
||
| *org-capture* | global-map | {{{kbd(C-c c)}}} |
|
||
| *org-cite-insert* | org-mode-map | {{{kbd(C-c b)}}} |
|
||
| *org-goto* | org-goto | {{{kbd(C-c C-j)}}} |
|
||
| *org-insert-link* | global-map | {{{kbd(C-c C-l)}}} |
|
||
| *org-narrow-to-table* | ctl-x-keymap | {{{kbd(C-x n t)}}} |
|
||
| *org-store-link* | global-map | {{{kbd(C-c l)}}} |
|
||
|-------------------------------+----------------+--------------------|
|
||
|
||
* [[info:emacs#Search][Search and replace (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:search-replace
|
||
:END:
|
||
|
||
** [[info:emacs#Regexp Replace][Regexp Replace (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:regexp-replace
|
||
:END:
|
||
|
||
Executing {{{kbd(M-x replace-regexp)}}} prompts for a regular expression
|
||
=REGEXP= and a substitution =NEWSTRING= to replace =REGEXP= with =NEWSTRING=.
|
||
The tools in section [[#sec:narrowing][Narrowing]] allow to limit the scope of {{{kbd(M-x
|
||
replace-regexp)}}}. A list pointing to relevant documentation is:
|
||
1. [[info:emacs#Regexp Replace][Regexp Replace (info)]] explains how the substitution handles references to
|
||
regular expression groups and lisp expressions.
|
||
2. [[info:emacs#Regexps][Syntax of Regular Expressions (info)]] describes regular expression features
|
||
for users.
|
||
3. [[info:elisp#Regular Expressions][Emacs Lisp Regular Expressions (info)]] describes regular expression features
|
||
for programmers.
|
||
4. [[https://www.emacswiki.org/][Emacs Wiki]] has an article on [[https://www.emacswiki.org/emacs/ReplaceRegexpWithLispExpressions][replacing regexp with lisp expressions]].
|
||
Table [[tab:replace-regexp-tranform]] tabulates how to perform example tasks by
|
||
means of {{{kbd(M-x replace-regexp)}}} using =(regular-expression transform)=
|
||
pairs.
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Example =replace-regexp= (regular-expression substitution) pairs]:
|
||
#+caption: Example =replace-regexp= (regular-expression substitution) pairs.
|
||
#+name: tab:replace-regexp-tranform
|
||
| task | regular expression | substitution |
|
||
|-----------------------------+---------------------------------+---------------|
|
||
| move dot two digits forward | =0\.\([[:digit:]][[:digit:]]\)= | =0\1.= |
|
||
| move dot two digits forward | =0\.\([0-9][0-9]\)= | =0\1.= |
|
||
| renumber grep/occur matches | =\(.+:\)= | =\,(1+ \#)._= |
|
||
|-----------------------------+---------------------------------+---------------|
|
||
|
||
** [[info:emacs#Incremental Search][Incremental Search (info)]] and [[info:emacs#Other Repeating Search][Other Search and Loop Commands (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:search
|
||
:END:
|
||
|
||
#+caption[Define =multi-occur-in-this-mode=]:
|
||
#+caption: Define =multi-occur-in-this-mode=.
|
||
#+name: lst:multi-occur-in-this-mode
|
||
#+begin_src emacs-lisp -n
|
||
;; 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))))
|
||
#+end_src
|
||
|
||
#+caption[Use =consult-line= from =isearch=]:
|
||
#+caption: Use =consult-line= from =isearch=.
|
||
#+name: lst:consult-line-from-isearch
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; 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))
|
||
#+end_src
|
||
|
||
#+caption[Use =occur= and =project-find-regexp= from =isearch=]:
|
||
#+caption: Use =occur= and =project-find-regexp= from =isearch=.
|
||
#+name: lst:occur-and-project-find-regexp-from-isearch
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; 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)
|
||
#+end_src
|
||
|
||
** [[https://www.genivia.com/get-ugrep.html][Ugrep]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:ugrep
|
||
:END:
|
||
|
||
[[https://www.genivia.com/get-ugrep.html][Ugrep]] is an ultra fast grep with interactive query UI and fuzzy search. The
|
||
pages [[https://github.com/Genivia/ugrep#emacs][Using ugrep within Emacs]] and [[https://manueluberti.eu/emacs/2022/08/07/emacs-ugrep/][Ugrep in Emacs]] show how to make Emacs use it
|
||
with [[info:emacs#Grep Searching][lgrep]] and [[info:emacs#Xref][xref]]. Listing [[lst:set-grep+xref-options][set =grep= and =xref= options]] shows my
|
||
implementation of the suggestions on those pages. *To do*:
|
||
1. None of the commands listed in [[info:emacs#Grep Searching][Searching with Grep under Emacs (info)]]
|
||
works on my system with the exception of ~lgrep~ when using [[https://www.genivia.com/get-ugrep.html][ugrep]].
|
||
2. None of the ~grep~, ~agrep~, ~egrep~, ~fgrep~, and ~glimpse~ builtins listed
|
||
in [[info:eshell#Built-ins][Eshell builtins (info)]] works.
|
||
|
||
#+caption[Set =grep= and =xref= options to use =ugrep= when available]:
|
||
#+caption: Set =grep= and =xref= options to use =ugrep= when available.
|
||
#+name: lst:set-grep+xref-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (executable-find "ugrep")
|
||
(with-eval-after-load 'grep
|
||
(setopt grep-template (string-join '("ugrep"
|
||
"--color=always"
|
||
"--decompress"
|
||
"--ignore-binary"
|
||
"--ignore-case"
|
||
"--include=\"<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>")))))
|
||
#+end_src
|
||
|
||
** [[https://github.com/mattiase/xr#readme][XR - Emacs regexp parser and analyzer]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:ensure-xr-installation
|
||
:END:
|
||
|
||
[[https://github.com/mattiase/xr#readme][XR]] converts Emacs regular expressions to the structured =rx= form, thus being an
|
||
inverse of =rx=. It can also find mistakes and questionable constructs inside
|
||
regexp strings.
|
||
|
||
#+caption[Ensure =xr= installation]:
|
||
#+caption: Ensure =xr= installation.
|
||
#+name: lst:ensure-xr-installation
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(ensure-package-installation 'xr)
|
||
#+end_src
|
||
|
||
* [[info:emacs#Version Control][Version Control (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:version-control
|
||
:END:
|
||
|
||
** [[info:ediff#Top][Ediff (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:ediff
|
||
:END:
|
||
|
||
Video links to complete the [[info:ediff#Top][ediff (info)]] manual are:
|
||
1. [[https://www.youtube.com/watch?v=6ZWp05OW1c0][Emergency Emacs]].
|
||
2. [[https://protesilaos.com/codelog/2020-04-10-emacs-smerge-ediff/][Use =smerge= and =ediff= to resolve file conflicts]].
|
||
Listing [[lst:setup-ediff]] configures =ediff= to display all its buffers in a
|
||
single frame and to make all text visible prior to ediffing Org buffers.
|
||
|
||
#+caption[Setup =ediff=]:
|
||
#+caption: Setup =ediff=.
|
||
#+name: lst:setup-ediff
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load '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))))
|
||
#+end_src
|
||
|
||
** [[https://git-scm.com/book/en/v2][Git]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:git
|
||
:END:
|
||
|
||
*** [[info:magit#Top][Magit (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:magit
|
||
:END:
|
||
|
||
[[info:magit#Top][Magit (info)]] is an Emacs interface to the version control system [[https://git-scm.com/book/en/v2][Git]] and the
|
||
snippet [[lst:ensure-magit-installation][below]] ensures its installation. Magit usage tips are:
|
||
1. [[https://stackoverflow.com/questions/tagged/magit?tab=Votes][Most upvoted Magit questions on Stack Overflow]].
|
||
2. [[https://stackoverflow.com/a/33644270][Abort an active rebase]] by pressing {{{kbd(r a)}}}.
|
||
I still have to understand [[info:magit#Pushing][Magit pushing (info)]] and to figure out how to use
|
||
src_emacs-lisp{(magit-push-current-to-pushremote)} or
|
||
src_emacs-lisp{(magit-push-current-to-upstream)}.
|
||
|
||
#+caption[Ensure =Magit= installation]:
|
||
#+caption: Ensure =Magit= installation
|
||
#+name: lst:ensure-magit-installation
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(ensure-package-installation 'magit)
|
||
#+end_src
|
||
|
||
*** Github quick setup
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:github-quick-setup
|
||
:END:
|
||
|
||
#+caption[Make a new repository on the command line]:
|
||
#+caption: Make a new repository on the command line.
|
||
#+name: lst:make-new-git-repo
|
||
#+begin_src shell -n :eval never
|
||
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
|
||
#+end_src
|
||
|
||
#+caption[Push an old git repository on the command line]:
|
||
#+caption: Push an old git repository on the command line.
|
||
#+name: lst:push-old-git-repo
|
||
#+begin_src shell -n :eval never
|
||
git remote add origin git@github.com:gav451/oglot.git
|
||
git branch -M main
|
||
git push -u origin main
|
||
#+end_src
|
||
|
||
*** Learn git
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:learn-git
|
||
:END:
|
||
|
||
Useful [[https://git-scm.com/book/en/v2][Git]] links are:
|
||
- [[https://sentry.io/answers/abort-a-merge-in-git/][Abort a merge in Git]]
|
||
- [[https://sentry.io/answers/change-the-uri-for-a-git-remote/][Change the URI for a Git remote]]
|
||
- [[https://sentry.io/answers/check-out-a-remote-branch-in-git/][Check out a remote branch in Git]]
|
||
- [[https://sentry.io/answers/clone-a-git-repository-to-a-specific-folder/][Clone a Git repository to a specific folder]]
|
||
- [[https://sentry.io/answers/clone-a-specific-git-repository-branch/][Clone a specific Git repository branch]]
|
||
- [[https://sentry.io/answers/create-a-new-git-branch-from-an-existing-branch/][Create a new Git branch from an existing branch]]
|
||
- [[https://sentry.io/answers/create-a-remote-branch-in-git/][Create a remote branch in Git]]
|
||
- [[https://sentry.io/answers/delete-a-commit-from-a-branch-in-git/][Delete a commit from a branch in Git]]
|
||
- [[https://sentry.io/answers/delete-a-file-from-a-git-repository/][Delete a file from a Git repository]]
|
||
- [[https://sentry.io/answers/delete-a-git-branch-locally-and-remotely/][Delete a Git branch locally and remotely]]
|
||
- [[https://sentry.io/answers/determine-the-origin-of-a-cloned-git-repository/][Determine the origin of a cloned Git repository]]
|
||
- [[https://sentry.io/answers/undo-the-most-recent-local-git-commits/][Undo the most recent local Git commits]]
|
||
|
||
*** [[https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/git-push-new-branch-remote-github-gitlab-upstream-example][List all Git branches]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:list-all-git-branches
|
||
:END:
|
||
|
||
#+caption[List all =git= branches]:
|
||
#+caption: List all =git= branches.
|
||
#+name: lst:list-all-git-branches
|
||
#+begin_src shell -n :exports both :results verbatim
|
||
git -C ~/VCS/oglot branch -a
|
||
#+end_src
|
||
|
||
#+RESULTS: lst:list-all-git-branches
|
||
: * main
|
||
: remotes/origin/main
|
||
|
||
* Reading
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading
|
||
:END:
|
||
|
||
** Reading [[https://en.wikipedia.org/wiki/DjVu][DjVu]] files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading-djvu-files
|
||
:END:
|
||
|
||
[[info:emacs#Document View][Document View (info)]] allows to read [[https://en.wikipedia.org/wiki/DjVu][DjVu]] files on condition that it can use the
|
||
command line [[https://en.wikipedia.org/wiki/DjVu][DjVu]] decoder =ddjvu= from the [[http://djvu.sourceforge.net/][DjVuLibre]] utilities as shown by the
|
||
code of src_emacs-lisp{(find-function 'doc-view-djvu->tiff-converter-ddjvu)}.
|
||
Use {{{kbd(+)}}} or {{{kbd(-)}}} to enlarge or shrink what =doc-view-mode=
|
||
displays and use {{{kbd(SPC)}}} or {{{kbd(S-SPC)}}} to scroll down or up through
|
||
when pages are larger than the =doc-view-mode= window.
|
||
|
||
** Reading EPUB files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading-epub-files
|
||
:END:
|
||
|
||
The package [[https://depp.brause.cc/nov.el/][nov.el]] provides a major mode for reading EPUB files in Emacs.
|
||
Listing [[lst:configure-nov]] configures [[https://depp.brause.cc/nov.el/][nov.el]].
|
||
|
||
#+caption[Configure =nov=]:
|
||
#+caption: Configure =nov=.
|
||
#+name: lst:configure-nov
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'nov)
|
||
(add-to-list 'auto-mode-alist `(,(rx ".epub" eos) . nov-mode)))
|
||
#+end_src
|
||
|
||
** Reading PDF files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading-pdf-files
|
||
:END:
|
||
|
||
The [[https://github.com/vedang/pdf-tools][pdf-tools]] package exploits the [[https://github.com/freedesktop/poppler][poppler]] library to render and to let you
|
||
annotate [[https://en.wikipedia.org/wiki/PDF][PDF]] files. It also exploits the [[https://wiki.contextgarden.net/SyncTeX][SyncTeX]] library to link anchors in [[https://en.wikipedia.org/wiki/PDF][PDF]]
|
||
files produced with LaTeX to the original LaTeX sources. Listing
|
||
[[lst:setup-pdf-tools]] loads =bookmark= in order to use [[https://github.com/vedang/pdf-tools][pdf-tools]] with
|
||
[[https://github.com/nicolaisingh/saveplace-pdf-view][saveplace-pdf-view]]. Building or rebuilding the =epdfinfo= executable that
|
||
serves the [[https://en.wikipedia.org/wiki/PDF][PDF]] files to Emacs is necessary after installation of [[https://github.com/vedang/pdf-tools][pdf-tools]] from
|
||
[[https://melpa.org/][MELPA]] or after each update of [[https://github.com/freedesktop/poppler][poppler]]. 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.
|
||
|
||
#+caption[Setup =pdf-tools=]:
|
||
#+caption: Setup =pdf-tools=.
|
||
#+name: lst:setup-pdf-tools
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'pdf-tools 'saveplace-pdf-view)
|
||
;; Enable `save-place-mode' after loading those two libraries.
|
||
(require 'bookmark) ; Prevent undocumented Emacs setup weirdness.
|
||
(require 'saveplace-pdf-view) ; Like documentated.
|
||
(save-place-mode +1) ; Like documentated.
|
||
;; 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))))
|
||
#+end_src
|
||
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Commands with local key bindings in ~pdf-view-mode~]:
|
||
#+caption: Commands with local key bindings in ~pdf-view-mode~.
|
||
#+name: tab:pdf-view-mode-commands-and-bindings
|
||
|---------------------------------+----------------|
|
||
| command | key binding |
|
||
|---------------------------------+----------------|
|
||
| *image-save* | {{{kbd(i o)}}} |
|
||
| *pdf-outline* | {{{kbd(o)}}} |
|
||
| *pdf-view-fit-height-to-window* | {{{kbd(H)}}} |
|
||
| *pdf-view-fit-width-to-window* | {{{kbd(W)}}} |
|
||
|---------------------------------+----------------|
|
||
|
||
** Reading news and outline files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading-news-outline-files
|
||
:END:
|
||
|
||
Listing [[lst:maybe-read-only]] implements heuristics to enable =read-only-mode= for
|
||
all =emacs-news-mode= and selected =outline-mode= files.
|
||
|
||
#+caption[Enable =read-only-mode= for news and selected outline files]:
|
||
#+caption: Enable =read-only-mode= for news and selected outline files.
|
||
#+name: lst:maybe-read-only
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(defun maybe-read-only-in-outline-mode ()
|
||
"Check whether to open a file in `outline-mode' with `read-only-mode' enabled."
|
||
;; GAV: Should be a toplevel function to call `message' here.
|
||
(let ((fnsd (buffer-file-name)))
|
||
;; GAV: `fnsd' is nil during Org export to LaTeX.
|
||
(when fnsd
|
||
(setq fnsd (file-name-nondirectory fnsd))
|
||
;; GAV: Handle files that do not open in `emacs-news-mode' from
|
||
;; the EMACS and ORG git repositories (`cl-member' works too).
|
||
(when (or (string= fnsd "EGLOT-NEWS")
|
||
(string= fnsd "ORG-NEWS")
|
||
(string= fnsd "PROBLEMS")
|
||
(string= fnsd "org.org")
|
||
(string= fnsd "org-guide.org")
|
||
(string= fnsd "org-manual.org"))
|
||
(message "Visit `%s' in `read-only-mode'" fnsd)
|
||
(read-only-mode +1)))))
|
||
|
||
(add-hook 'outline-mode-hook #'maybe-read-only-in-outline-mode)
|
||
(add-hook 'emacs-news-mode-hook (lambda () (read-only-mode +1)))
|
||
#+end_src
|
||
|
||
* Writing
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing
|
||
:END:
|
||
|
||
** Writing LaTeX files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-latex-files
|
||
:END:
|
||
|
||
Loading =tex.el= immediately instead of lazily ensures proper initialization of
|
||
[[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]]. In particular, the ~TeX-master~ safe local variable in the =tex.el=
|
||
elisp library file has no autoload cookie. Without prior loading of =tex.el=,
|
||
Emacs will complain that ~TeX-master~ is no safe local variable in case it reads
|
||
a LaTeX file that sets ~TeX-master~. The list below summarizes the main [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]]
|
||
configuration objectives:
|
||
1. Listing [[lst:set-tex-options]] ensures installation of [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] and initializes
|
||
[[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] properly for Lua-TeX or Lua-LaTeX. Listing [[lst:set-tex-options]]
|
||
also enables *indenting between square brackets* which is a recent feature.
|
||
2. Listing [[lst:set-latex-related-options]] configures the Emacs =bibtex= library
|
||
to use the LaTeX BibTeX dialect for backwards compatibility, disables font
|
||
scaling of section titles, and configures LaTeX for a full featured =LaTeX-section-command=.
|
||
|
||
#+caption[Ensure =AUCTeX= installation and set TeX options]:
|
||
#+caption: Ensure =AUCTeX= installation and set TeX options.
|
||
#+name: lst:set-tex-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'auctex)
|
||
;; Use `require' to make `TeX-master' a safe local variable.
|
||
(when (require 'tex nil 'noerror)
|
||
(setopt
|
||
TeX-auto-save t
|
||
TeX-engine 'luatex
|
||
TeX-indent-close-delimiters "]"
|
||
TeX-indent-open-delimiters "["
|
||
TeX-parse-self t
|
||
;; Disable `TeX-electric-math' to prevent collisions with `smartparens'.
|
||
TeX-electric-math nil)))
|
||
#+end_src
|
||
|
||
#+caption[Set BibTeX and LaTeX options]:
|
||
#+caption: Set BibTeX and LaTeX options.
|
||
#+name: lst:set-latex-related-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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)))
|
||
#+end_src
|
||
|
||
** Writing [[https://www.markdownguide.org/][Markdown]] files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-markdown-files
|
||
:END:
|
||
|
||
#+caption[Configure =markdown-mode=]:
|
||
#+caption: Configure =markdown-mode=.
|
||
#+name: lst:configure-markdown-mode
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'markdown-mode) t)
|
||
#+end_src
|
||
|
||
** Writing [[info:org#Top][Org (info)]] files and [[info:org#Activation][Org activation (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-org-files
|
||
:END:
|
||
|
||
Listing [[lst:bind-org-commands]] activates [[info:org#Top][Org (info)]] by means of global key
|
||
bindings.
|
||
|
||
#+caption[Bind =Org= commands globally]:
|
||
#+caption: Bind =Org= commands globally.
|
||
#+name: lst:bind-org-commands
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
(global-set-key (kbd "C-c a") #'org-agenda)
|
||
(global-set-key (kbd "C-c c") #'org-capture)
|
||
(global-set-key (kbd "C-c l") #'org-store-link))
|
||
#+end_src
|
||
|
||
Links to Org-mode videos are:
|
||
1. [[yt:o6rE18Mxu9U][Analyze Your Time with Org Mode Clocktables]]
|
||
2. [[yt:EgOBBiomfGo][Basic Task Management with Org: Checklists, TODOs, and Org-Agenda]]
|
||
3. [[yt:oJTwQvgfgMM][Emacs Org-mode: a system for note-taking and project planning]]
|
||
4. [[yt:SzA2YODtgK4][Getting Started With Org Mode]]
|
||
5. [[yt:zqAYHWv36X0][Org Mode Time and Task Tools]]
|
||
6. [[yt:WUsgmQjMB6Q][Orgmode Workflow 3 (with Python Source Blocks)]]
|
||
|
||
*** Setup Org
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:setup-org
|
||
:END:
|
||
|
||
I have divided the initial [[https://orgmode.org/][Org-mode]] setup into ten listings. Here, follows a
|
||
list detailing and motivating each listing:
|
||
1. Listing [[lst:basic-org-setup]] handles basic [[https://orgmode.org/][Org-mode]] setup.
|
||
2. Listing [[lst:org-capture-templates]] initializes a simple capture template.
|
||
3. Listing [[lst:setup-org-babel]] sets ~ob-core~, ~ob-latex~, and ~ob-lisp~ options.
|
||
See [[info:org#Working with Source Code][working with source code (info)]] for Org Babel information.
|
||
4. Listing [[lst:setup-org-mode-map-1]] defines the =org-electric-dollar= command
|
||
and binds it into =org-mode-map=.
|
||
5. Listing [[lst:setup-org-mode-map-2]] defines the =org-insert-source-block=
|
||
command and binds it into =org-mode-map=.
|
||
6. Listing [[lst:setup-org-src]] facilitates [[info:org#Editing Source Code][editing source code blocks]], and it
|
||
provides functions to change the indentation of all =org-src-mode= blocks.
|
||
7. Listing [[lst:setup-ob-python]] allows to pretty-print Python session source
|
||
block values with [[https://github.com/psf/black#readme][black]] instead of [[https://docs.python.org/3/library/pprint.html][pprint]]. 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!
|
||
8. Listing [[lst:set-org-export-options]] selects the =non-intrusive= expert user
|
||
interface for export dispatching.
|
||
9. Listing [[lst:setup-org-for-lualatex-preview]] allows [[https://orgmode.org/][Org-mode]] to generate
|
||
preview =png= images with Lua-LaTeX and ImageMagick.
|
||
10. Listing [[lst:set-ox-latex-options-for-lualatex-export]] configures [[https://orgmode.org/][Org-mode]] to
|
||
generate output for Lua-LaTeX.
|
||
|
||
#+caption[Basic =Org= setup]:
|
||
#+caption: Basic =Org= setup.
|
||
#+name: lst:basic-org-setup
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'org
|
||
(setopt
|
||
org-babel-load-languages `((calc . t)
|
||
(emacs-lisp . t)
|
||
(eshell . t)
|
||
(latex . t)
|
||
(lisp . t)
|
||
(lua . ,(fboundp 'lua-mode))
|
||
(org . t)
|
||
(perl . t)
|
||
;; Beware of circular Python dependencies.
|
||
(python . t)
|
||
(ruby . t)
|
||
(shell . t))
|
||
org-export-backends '(ascii beamer html latex texinfo)
|
||
org-file-apps '((auto-mode . emacs)
|
||
(directory . emacs)
|
||
("\\.mm\\'" . default)
|
||
("\\.x?html?\\'" . default)
|
||
("\\.pdf\\'" . emacs))
|
||
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))
|
||
org-modules (list 'ol-bibtex 'ol-doi 'ol-eww 'ol-info
|
||
'org-id 'org-protocol 'org-tempo)
|
||
org-return-follows-link t
|
||
org-use-property-inheritance t)
|
||
|
||
(keymap-set org-mode-map "M-q" #'org-fill-paragraph))
|
||
#+end_src
|
||
|
||
#+caption[Setup =org-capture-templates=]:
|
||
#+caption: Setup =org-capture-templates=.
|
||
#+name: lst:org-capture-templates
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'org-capture
|
||
(setopt org-capture-templates
|
||
'(("f" "Flat capture database" entry (file "flat.org")
|
||
"* %^{Prompt}t %?
|
||
%a"))))
|
||
#+end_src
|
||
|
||
#+caption[Set =org-babel= options]:
|
||
#+caption: Set =org-babel= options.
|
||
#+name: lst:setup-org-babel
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'ob-core
|
||
(setopt org-confirm-babel-evaluate nil))
|
||
|
||
(with-eval-after-load 'ob-latex
|
||
(setopt org-babel-latex-preamble
|
||
(lambda (_)
|
||
"\\documentclass[preview]{standalone}\n")))
|
||
|
||
(with-eval-after-load 'ob-lisp
|
||
(when (package-installed-p 'sly)
|
||
(setopt org-babel-lisp-eval-fn #'sly-eval)))
|
||
#+end_src
|
||
|
||
#+caption[Setup =org-mode-map= 1]:
|
||
#+caption: Setup =org-mode-map= 1.
|
||
#+name: lst:setup-org-mode-map-1
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; 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)))
|
||
|
||
(with-eval-after-load 'org
|
||
(keymap-set org-mode-map "$" #'org-electric-dollar))
|
||
#+end_src
|
||
|
||
#+caption[Setup =org-mode-map= 2]:
|
||
#+caption: Setup =org-mode-map= 2.
|
||
#+name: lst:setup-org-mode-map-2
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; 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"
|
||
"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))
|
||
#+end_src
|
||
|
||
#+caption[Setup =org-src=]:
|
||
#+caption: Setup =org-src=.
|
||
#+name: lst:setup-org-src
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'org-src
|
||
;; https://list.orgmode.org/c3cc4ff1fcfb3bc741df89a3f45be30e@posteo.net/
|
||
(setopt org-edit-src-content-indentation 0
|
||
org-src-preserve-indentation nil
|
||
org-src-window-setup 'current-window)
|
||
|
||
(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)))))
|
||
|
||
(defun zero-all-org-src-blocks-indentation ()
|
||
"Remove the indentation of all `org-src-mode' blocks without `-i' switch."
|
||
(interactive)
|
||
(when (derived-mode-p 'org-mode)
|
||
(save-excursion
|
||
(org-babel-map-src-blocks
|
||
nil
|
||
(unless (or (string-match "-i " switches)
|
||
(string-match (rx (or bos bol) graph) body))
|
||
(when-let ((new (with-temp-buffer
|
||
(insert body)
|
||
(org-do-remove-indentation)
|
||
(buffer-string))))
|
||
(goto-char beg-body)
|
||
(delete-region beg-body end-body)
|
||
(insert new))))))))
|
||
#+end_src
|
||
|
||
#+caption[Setup =ob-python=]:
|
||
#+caption: Setup =ob-python=. This snippet may break in the future!
|
||
#+name: lst:setup-ob-python
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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))"))
|
||
#+end_src
|
||
|
||
#+caption[Set =org-export= options]:
|
||
#+caption: Set =org-export= options.
|
||
#+name: lst:set-org-export-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'ox
|
||
(setopt org-export-dispatch-use-expert-ui t))
|
||
#+end_src
|
||
|
||
#+caption[Setup =org= for preview using Lua-LaTeX]:
|
||
#+caption: Setup =org= for preview using Lua-LaTeX.
|
||
#+name: lst:setup-org-for-lualatex-preview
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'org
|
||
;; https://list.orgmode.org/87o84fd4oi.fsf@posteo.net/
|
||
(add-to-list
|
||
'org-preview-latex-process-alist
|
||
'(luamagick
|
||
:programs ("lualatex" "convert")
|
||
:description "pdf > png"
|
||
:message "you need to install lualatex and imagemagick."
|
||
:image-input-type "pdf"
|
||
:image-output-type "png"
|
||
:image-size-adjust (1.0 . 1.0)
|
||
:post-clean nil
|
||
:latex-header nil
|
||
:latex-compiler
|
||
("lualatex -interaction nonstopmode -output-directory %o %f")
|
||
:image-converter
|
||
("convert -density %D -trim -antialias %f -quality 100 %O")))
|
||
(setopt org-preview-latex-default-process 'luamagick))
|
||
#+end_src
|
||
|
||
#+caption[Set =ox-latex= options for export to Lua-LaTeX]:
|
||
#+caption: Set =ox-latex= options for export to Lua-LaTeX.
|
||
#+name: lst:set-ox-latex-options-for-lualatex-export
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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))
|
||
#+end_src
|
||
|
||
*** [[info:org#In-buffer Settings][Buffer properties]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:buffer-properties
|
||
:END:
|
||
|
||
#+caption[Buffer properties]:
|
||
#+caption: Buffer properties.
|
||
#+name: lst:buffer-properties
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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)))))))
|
||
#+end_src
|
||
|
||
*** Org introspection
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:org-introspection
|
||
:END:
|
||
|
||
1. Listing [[lst:org-babel-active-languages]] produces a list containing language
|
||
names that have ~org-babel-execute:LANGUAGE~ functions.
|
||
2. Listing [[lst:get-in-buffer-settings]] and [[lst:get-in-buffer-settings-result]] show
|
||
how to access [[info:org#In-buffer Settings][In-buffer Settings (info)]].
|
||
|
||
#+caption[Find active Org Babel languages]:
|
||
#+caption: Find active Org Babel languages.
|
||
#+name: lst:org-babel-active-languages
|
||
#+header: :wrap "src emacs-lisp -n :eval never :tangle no"
|
||
#+begin_src emacs-lisp -n :exports both :results value pp :tangle no
|
||
(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 (sort (org-babel-active-languages)))
|
||
#+end_src
|
||
|
||
#+caption[Active Org Babel languages]:
|
||
#+caption: Active Org Babel languages.
|
||
#+RESULTS: lst:org-babel-active-languages
|
||
#+begin_src emacs-lisp -n :eval never :tangle no
|
||
(("calc") ("emacs-lisp") ("eshell") ("latex") ("lisp") ("org")
|
||
("perl") ("python") ("ruby") ("shell"))
|
||
#+end_src
|
||
|
||
#+caption[Get "in-buffer" settings]:
|
||
#+caption: Get "in-buffer" settings.
|
||
#+name: lst:get-in-buffer-settings
|
||
#+header: :wrap "src emacs-lisp -n :eval never :tangle no"
|
||
#+begin_src emacs-lisp -n :exports both :results value pp :tangle no
|
||
(org-collect-keywords '("title" "author" "latex_class"))
|
||
#+end_src
|
||
|
||
#+caption[Get "in-buffer" settings result]:
|
||
#+caption: Get "in-buffer" settings result.
|
||
#+name: lst:get-in-buffer-settings-result
|
||
#+RESULTS: lst:get-in-buffer-settings
|
||
#+begin_src emacs-lisp -n :eval never :tangle no
|
||
(("TITLE" "Emacs setup for use with LaTeX, Lisp, Org, and Python")
|
||
("AUTHOR" "Gerard Vermeulen") ("LATEX_CLASS" "article"))
|
||
#+end_src
|
||
|
||
*** [[https://github.com/bdarcus/citar][Citar: citing bibliography]] with [[https://orgmode.org/][Org Mode]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:citing-bibliography
|
||
:END:
|
||
|
||
[[https://github.com/bdarcus/citar][Citar]] is a completing-read front-end to browse and act on BibTeX, BibLaTeX, as
|
||
well as CSL JSON bibliographic data with LaTeX, markdown, and org-cite editing
|
||
support. [[https://github.com/bdarcus/citar][Citar]] in combination with [[https://github.com/minad/vertico][Vertico]] provides quick filtering and
|
||
selecting of bibliographic entries from the minibuffer.
|
||
|
||
The article [[https://kristofferbalintona.me/posts/202206141852/][Citations in org-mode: Org-cite and Citar]] tries to walk you from
|
||
understanding the general context (bibliography producer, text processor, text
|
||
to product converter) to an Emacs setup. Listing [[lst:set-oc+citar-options]] shows
|
||
the =oc= and =citar= setup with binding of {{{kbd(C-c b)}}} to
|
||
src_emacs-lisp{(call-interactively 'org-cite-insert)} in =org-mode-map=.
|
||
|
||
#+caption[Set =oc= and =citar= options]:
|
||
#+caption: Set =oc= and =citar= options.
|
||
#+name: lst:set-oc+citar-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'citar)
|
||
(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)))))
|
||
#+end_src
|
||
|
||
Ref. [cite:@Schulte.MCSE.2011.41] shows that [[https://orgmode.org/][Org-mode]] is a simple, plain-text
|
||
markup language for hierarchical documents allowing intermingling of data, code,
|
||
and prose. It serves as an example link to show how to use [[https://github.com/bdarcus/citar][Citar]] within this
|
||
setup, provided that =citar-bibliography= and =citar-library-paths= point to
|
||
valid directories and files. In an [[https://orgmode.org/][Org-mode]] buffer this setup allows to:
|
||
1. Insert one or more [[https://orgmode.org/][Org-mode]] cite links:
|
||
1. Type {{{kbd(C-c b)}}} or {{{kbd(M-x org-cite-insert)}}}.
|
||
2. Choose one or multiple citations from the bibliography:
|
||
1. by navigating to an item and selecting it with {{{kbd(RET)}}}
|
||
2. by repeatingly navigating to an item and preselecting it with
|
||
{{{kbd(TAB)}}}. Select all preselected items with {{{kbd(RET)}}}.
|
||
2. View an electronic copy or the DOI of an [[https://orgmode.org/][Org-mode]] cite link:
|
||
1. Click on the [[https://orgmode.org/][Org-mode]] cite link.
|
||
2. Navigate to the corresponding resource.
|
||
3. Select it.
|
||
3. The following =citar= functions may be useful too:
|
||
1. src_emacs-lisp{(call-interactively 'citar-insert-citation)}.
|
||
2. src_emacs-lisp[:results silent]{(call-interactively 'citar-open)}.
|
||
|
||
*** [[https://github.com/tecosaur/engrave-faces#readme][Engrave Faces]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:engrave-faces
|
||
:END:
|
||
|
||
This package has a front-end that processes fontified buffers and pipes its
|
||
output into one of three back-ends for export to LaTeX, HTML and text with ANSI
|
||
escape codes. It is an alternative to =minted= in case of LaTeX export or to
|
||
=htmlized= in case of HTML export.
|
||
|
||
The listings below work around one issue with spacing in the =pdf= output
|
||
resulting from use of the =engraved= source block back-end during =LaTeX=
|
||
export. They increase the quality of the export results of this [[file:README.org][README.org]] and
|
||
the user-friendliness of this facility to a level acceptable for my workflow:
|
||
1. The default Org export configuration for engraving listings produces =pdf=
|
||
output with a non-equidistant interline spacing in floating listings (those
|
||
with captions) and equidistant interline spacing in non-floating =tcolorbox=
|
||
environments (those without captions). I have traced the non-equidistant
|
||
interline spacing back to the =breakable= option of =Code= environments
|
||
inside =listing= environments. However, =tcolorbox= requires =breakable= for
|
||
listings that do not fit on a page. The filter =org-latex-engraved-source-block-filter= in listing
|
||
[[lst:org-latex-engraved-source-block-filter]] and the =org-latex-engraved-preamble= customization in listing
|
||
[[lst:smart-latex-engrave-org-source-blocks]] allow to engrave =org-mode= source
|
||
blocks to "floating unbreakable with caption" or "non-floating breakable
|
||
without caption" LaTeX environments. This is a partial work-around, but part
|
||
of my workflow.
|
||
2. Listing [[lst:ox-engraved-emacs-lisp-setup]] shows how to use this configuration.
|
||
|
||
#+caption[Engrave to floating unbreakable or non-floating breakable environments]:
|
||
#+caption: Define an =org-export= filter function to engrave =org-src-mode=
|
||
#+caption: blocks to floating unbreakable LaTeX environments or non-floating
|
||
#+caption: breakable LaTeX environments.
|
||
#+name: lst:org-latex-engraved-source-block-filter
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(ensure-package-installation 'engrave-faces)
|
||
|
||
(defun org-latex-engraved-source-block-filter (data _backend _info)
|
||
"Replace \"Code\" with \"Breakable\" in non-floating 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")))))
|
||
#+end_src
|
||
|
||
#+caption[Smart LaTeX engraving of =org-src-mode= blocks]:
|
||
#+caption: Smart LaTeX engraving of =org-src-mode= blocks.
|
||
#+name: lst:smart-latex-engrave-org-source-blocks
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(defun smart-latex-engrave-org-source-blocks ()
|
||
"Enable smart LaTeX engraving of `org-src-mode' blocks.
|
||
|
||
Sets backend and preamble locally to support floating unbreakable LaTeX
|
||
environments and non-floating breakable LaTeX environments by means of
|
||
`org-latex-engraved-source-block-filter'."
|
||
(interactive)
|
||
(when (require 'ox-latex nil 'noerror)
|
||
(setq-local org-latex-src-block-backend 'engraved
|
||
org-latex-engraved-preamble "\\usepackage{fvextra}
|
||
[FVEXTRA-SETUP]
|
||
|
||
% Make code and line numbers normalsize. Make line numbers grey.
|
||
\\renewcommand\\theFancyVerbLine{
|
||
\\normalsize\\color{black!40!white}\\arabic{FancyVerbLine}}
|
||
% 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]")))
|
||
#+end_src
|
||
|
||
#+caption[Emacs setup to use =engrave-faces-latex= in =org-mode= smartly]:
|
||
#+caption: Emacs setup to use =engrave-faces-latex= in =org-mode= smartly.
|
||
#+name: lst:ox-engraved-emacs-lisp-setup
|
||
#+begin_src emacs-lisp -n :exports code :results none :tangle no
|
||
(with-eval-after-load 'ox-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)))
|
||
#+end_src
|
||
|
||
*** [[https://github.com/flexibeast/org-vcard#org-vcard---org-mode-support-for-vcard-export-and-import][Org-mode support for vCard import and export]] :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:org-vcard
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
|
||
Import/export =vcf= files (Android phones) from/to Org-mode linear (level 1)
|
||
headline lists with the data in property drawers.
|
||
|
||
#+caption[Install =org-vcard= to read =vcf= files=]:
|
||
#+caption: Install =org-vcard= to read =vcf= files=.
|
||
#+name: lst:org-vcard
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(ensure-package-installation 'org-vcard)
|
||
#+end_src
|
||
|
||
*** [[info:org#Adding Hyperlink Types][Making Org hyperlink types (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:make-org-hyperlink-types
|
||
:END:
|
||
|
||
The listings below implement or reimplement three groups of =org-link= types:
|
||
1. Listing [[lst:org-ref-like-org-link-types]] defines =org-link= types for
|
||
backwards compatibility with [[https://github.com/jkitchin/org-ref][org-ref]].
|
||
2. Listing [[lst:define-org-pdfview-link-type]] uses ideas from the definition of
|
||
=docview-org-link= and =doi-org-link= types to define an =pdfview-org-link=
|
||
type for use with [[https://github.com/vedang/pdf-tools][pdf-tools]].
|
||
3. Listing [[lst:patch-org-info-link-type]] patches the function =org-info-export=
|
||
and listing [[lst:emacs-lisp-setup-buffer-local-ol-info]] sets the constants ~org-info-emacs-documents~ and ~org-info-other-documents~ as buffer local
|
||
variables in order to export the =info-org-link= types in this document to =html= and LaTeX correctly.
|
||
|
||
#+caption[Define =org-link= types for backwards compatibility with =org-ref=]:
|
||
#+caption: Define =org-link= types for backwards compatibility with =org-ref=.
|
||
#+name: lst:org-ref-like-org-link-types
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'ol
|
||
(org-link-set-parameters "ac*" :export #'org-ref-ac*-export)
|
||
(org-link-set-parameters "cite" :export #'org-ref-cite-export)
|
||
(org-link-set-parameters "eqref" :export #'org-ref-eqref-export)
|
||
(org-link-set-parameters "hyperlink" :export #'org-ref-hyperlink-export)
|
||
(org-link-set-parameters "label" :export #'org-ref-label-export)
|
||
(org-link-set-parameters "ref" :export #'org-ref-ref-export)
|
||
|
||
(defun org-ref-ac*-export (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\gls*{%s}" path))
|
||
(_ path)))
|
||
|
||
(defun org-ref-cite-export (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\cite{%s}" path))
|
||
(_ path)))
|
||
|
||
(defun org-ref-eqref-export (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\eqref{%s}" path))
|
||
(_ path)))
|
||
|
||
(defun org-ref-hyperlink-export (path desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\hyperlink{%s}{%s}" path desc))
|
||
(_ path)))
|
||
|
||
(defun org-ref-label-export (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\label{%s}" path))
|
||
(_ path)))
|
||
|
||
(defun org-ref-ref-export (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\ref{%s}" path))
|
||
(_ path))))
|
||
#+end_src
|
||
|
||
#+caption[Define an =org-link= type for =pdf-tools=]:
|
||
#+caption: Define an =org-link= type for =pdf-tools=.
|
||
#+name: lst:define-org-pdfview-link-type
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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 (&optional _interactive?)
|
||
"Store a \"pdfview\" type link."
|
||
(when (eq major-mode 'pdf-view-mode)
|
||
(let* ((path buffer-file-name)
|
||
(page (pdf-view-current-page))
|
||
(text (concat "pdfview:" path "::" (number-to-string page))))
|
||
(org-link-store-props
|
||
:type "pdfview"
|
||
:link text
|
||
: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))
|
||
#+end_src
|
||
|
||
#+caption[Patch =ol-info=]:
|
||
#+caption: Patch =ol-info=.
|
||
#+name: lst:patch-org-info-link-type
|
||
#+begin_src emacs-lisp -n :exports code :results silent
|
||
(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)))))
|
||
#+end_src
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Patch buffer local =ol-info=]:
|
||
#+caption: Patch buffer local =ol-info=.
|
||
#+name: lst:emacs-lisp-setup-buffer-local-ol-info
|
||
#+begin_src emacs-lisp -n :exports code :results none :tangle no
|
||
(with-eval-after-load 'ol-info
|
||
(make-variable-buffer-local 'org-info-emacs-documents)
|
||
(setq org-info-emacs-documents (delete "org" org-info-emacs-documents))
|
||
(make-variable-buffer-local 'org-info-other-documents)
|
||
(setq org-info-other-documents
|
||
(cl-union
|
||
'(("consult" . "https://github.com/minad/consult")
|
||
("magit" . "https://magit.vc/manual/magit.html")
|
||
("marginalia" . "https://github.com/minad/marginalia")
|
||
("org" . "https://orgmode.org/org.html")
|
||
("sly" . "https://joaotavora.github.io/sly/")
|
||
("vertico" . "https://github.com/minad/vertico"))
|
||
org-info-other-documents :test #'equal)))
|
||
#+end_src
|
||
|
||
*** [[https://bitspook.in/blog/extending-org-mode-to-handle-youtube-links/][Extending org-mode to handle YouTube links]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:make-org-yt-link-type
|
||
:END:
|
||
|
||
Listing [[lst:define-org-yt-link-type]] implements code to open the link to the
|
||
following YouTube video [[yt:eaZUZCzaIgw][Extending org-mode to handle YouTube links]] modifying
|
||
code from the following [[https://raw.githubusercontent.com/bitspook/spookmax.d/master/readme.org][Emacs setup]].
|
||
|
||
Opening [[yt:eaZUZCzaIgw][Extending org-mode to handle YouTube links]] may fail due to an old
|
||
(fixed) bug in =mpv=. However, the link [[yt:48JlgiBpw_I][Absolute Beginner's Guide to Emacs]]
|
||
works always.
|
||
|
||
NOTE: Listing [[lst:define-org-yt-link-type][define org-yt-link type]] does only implement normal =html= links
|
||
instead of embedded video links, but listing [[lst:howto-embed-yt]] shows a working
|
||
=html= example of how to embed a YouTube video in an inline frame element.
|
||
|
||
#+caption[Define an =org-link= type for =YouTube=]:
|
||
#+caption: Define an =org-link= type for =YouTube=.
|
||
#+name: lst:define-org-yt-link-type
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'emms)
|
||
;; https://bitspook.in/blog/extending-org-mode-to-handle-youtube-paths/
|
||
(defun org-yt-emms-open (path)
|
||
"Open an \"YouTube\" PATH link with `emms' using \"mpv\"."
|
||
(let ((link (format "https://www.youtube.com/watch?v=%s" path)))
|
||
(require 'emms nil 'noerror)
|
||
(emms-add-url link)
|
||
(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 ((link (format "https://www.youtube.com/watch?v=%s" path)))
|
||
(browse-url link)))
|
||
|
||
(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 "<a href=\"https://www.youtube.com/watch?v=%s\">%s</a>" path desc))
|
||
(`latex
|
||
(format "\\href{https://www.youtube.com/watch?v=%s}{%s}" path desc))
|
||
(_ path)))
|
||
|
||
(with-eval-after-load 'ol
|
||
(org-link-set-parameters
|
||
"yt" :follow #'org-yt-emms-open :export #'org-yt-export)))
|
||
#+end_src
|
||
|
||
#+caption[How to embed a =YouTube= video in =HTML=]:
|
||
#+caption: How to embed a =YouTube= video in =HTML=.
|
||
#+name: lst:howto-embed-yt
|
||
#+begin_src html -n :exports code :tangle yegge.html
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>ChatGPT: How to embed a YouTube video in HTML</title>
|
||
</head>
|
||
<body>
|
||
<h1>Steve Yegge embedded YouTube video</h1>
|
||
<iframe width="630" height="420"
|
||
src="https://www.youtube.com/embed/lkIicfzPBys"
|
||
allowfullscreen></iframe>
|
||
</body>
|
||
</html>
|
||
#+end_src
|
||
|
||
*** [[https://tecosaur.github.io/emacs-config/#translate-capital-keywords][Translate capital keywords (old) to lower case (new)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:convert-upper-to-lower-case-keywords
|
||
:END:
|
||
|
||
#+caption[Convert upper to lower case keywords]:
|
||
#+caption: Convert upper to lower case keywords.
|
||
#+name: lst:convert-upper-to-lower-case-keywords
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
;; https://tecosaur.github.io/emacs-config/#translate-capital-keywords
|
||
(defun org-syntax-convert-keyword-case-to-lower ()
|
||
"Convert all #+KEYWORDS to #+keywords."
|
||
(interactive)
|
||
(when (derived-mode-p 'org-mode)
|
||
(save-excursion
|
||
(goto-char (point-min))
|
||
(let ((count 0)
|
||
(case-fold-search nil))
|
||
(while (re-search-forward "^[ \t]*#\\+[A-Z_]+" nil t)
|
||
(unless (s-matches-p "RESULTS" (match-string 0))
|
||
(replace-match (downcase (match-string 0)) t)
|
||
(setq count (1+ count))))
|
||
(message "Replaced %d keywords" count))))))
|
||
#+end_src
|
||
|
||
*** [[https://lists.gnu.org/archive/html/emacs-orgmode/2016-07/msg00394.html][Evaluate specific source blocks at load-time]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:load-time-specific-source-block-evaluation
|
||
:END:
|
||
|
||
[[https://emacs.stackexchange.com/questions/12938/how-can-i-evaluate-elisp-in-an-orgmode-file-when-it-is-opened][How to do load time source block evaluation]]
|
||
|
||
#+caption[Evaluate specific source blocks at load-time]:
|
||
#+caption: Evaluate specific source blocks at load-time.
|
||
#+name: lst:load-time-specific-source-block-evaluation
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
(defun org-eval-named-blocks-with-infix (infix)
|
||
"Evaluate all source blocks having INFIX in their name."
|
||
(when (eq major-mode 'org-mode)
|
||
(let ((blocks
|
||
(org-element-map
|
||
(org-element-parse-buffer 'greater-element nil) 'src-block
|
||
(lambda (block)
|
||
(when-let ((name (org-element-property :name block)))
|
||
(when (string-match-p infix name) block))))))
|
||
(dolist (block blocks)
|
||
(goto-char (org-element-property :begin block))
|
||
(org-babel-execute-src-block)))))
|
||
|
||
(defun org-eval-emacs-lisp-setup-blocks ()
|
||
"Evaluate all source blocks having \"emacs-lisp-setup\" in their name."
|
||
(interactive)
|
||
(org-eval-named-blocks-with-infix "emacs-lisp-setup"))
|
||
|
||
(defun org-eval-python-setup-blocks ()
|
||
"Evaluate all source blocks having \"python-setup\" in their name."
|
||
(interactive)
|
||
(org-eval-named-blocks-with-infix "python-setup"))
|
||
|
||
;; Emacs looks for "Local variables:" after the last "form-feed".
|
||
(add-to-list 'safe-local-eval-forms '(org-eval-emacs-lisp-setup-blocks))
|
||
(add-to-list 'safe-local-eval-forms '(org-eval-python-setup-blocks)))
|
||
#+end_src
|
||
|
||
*** [[info:org#Macro Replacement][Org-mode macro utilities]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:org-macro-utilities
|
||
:END:
|
||
|
||
Listing [[lst:by-backend-kbd-org-macro]] defines the Emacs Lisp utilities to
|
||
define the [[https://orgmode.org/][Org mode]] =kbd= macro in listing
|
||
[[lst:source-file-export-keyword-settings]].
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Define Emacs Lisp utilities to define the =Org-mode= =kbd= macro]:
|
||
#+caption: Define Emacs Lisp utilities to define the =Org-mode= =kbd= macro.
|
||
#+name: lst:by-backend-kbd-org-macro
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
(when (ensure-package-installation 'htmlize)
|
||
(autoload 'htmlize-protect-string "htmlize" nil t))
|
||
|
||
;; https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-LaTeX.html
|
||
(defmacro by-backend (&rest body)
|
||
"Help for org-export backend dependent execution."
|
||
`(cl-case ',(bound-and-true-p org-export-current-backend) ,@body))
|
||
|
||
(defun by-backend-kbd-org-macro (keys)
|
||
"Help for an org-export backend dependent \"#+macro: kbd\"."
|
||
(by-backend
|
||
(ascii (format "`kbd(%s)'" keys))
|
||
(html (format "@@html:<kbd>%s</kbd>@@" (htmlize-protect-string keys)))
|
||
(latex (format "@@latex:\\colorbox{PowderBlue}{\\texttt{%s}}@@" keys)))))
|
||
#+end_src
|
||
|
||
*** Handle Python exceptions in ~ob-python~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:handle-python-exceptions
|
||
:END:
|
||
|
||
Python exceptions are never visible while executing Python ~org-src-mode~
|
||
blocks. Redirecting Python exceptions is a way to make them visible. Listing
|
||
[[lst:redirect-python-exceptions]] redirects Python exceptions to the file
|
||
~stderr.arm~, listing [[lst:open-python-exceptions]] opens the file ~stderr.arm~ in
|
||
~auto-revert-mode~, and listing [[lst:demo-python-exceptions]] demonstrates the
|
||
redirection of Python exceptions to the file ~stderr.arm~.
|
||
|
||
#+caption[Redirect Python exceptions to a file]:
|
||
#+caption: Redirect Python exceptions to a file:
|
||
#+name: lst:redirect-python-exceptions
|
||
#+begin_src python -i -n :results silent :session :tangle no
|
||
import sys
|
||
|
||
file = open("stderr.arm", "w")
|
||
sys.stderr = file
|
||
#+end_src
|
||
|
||
#+caption[Open the redirected file in ~auto-revert-mode~]:
|
||
#+caption: Open the redirected file in ~auto-revert-mode~.
|
||
#+name: lst:open-python-exceptions
|
||
#+begin_src emacs-lisp -n :results silent :tangle no
|
||
(find-file "stderr.arm")
|
||
(auto-revert-mode)
|
||
#+end_src
|
||
|
||
#+caption[Demonstrate the redirection of Python exceptions to a file]:
|
||
#+caption: Demonstrate the redirectio of Python exceptions to a file.
|
||
#+name: lst:demo-python-exceptions
|
||
#+begin_src python -i -n :results silent :session :tangle no
|
||
variable
|
||
#+end_src
|
||
|
||
*** [[https://orgmode.org/worg/org-tests/index.html][Testing Org]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:testing-org
|
||
:END:
|
||
|
||
Listing [[lst:batch-org-testing]] shows the recommended way: use =make test= in a
|
||
clean Org git repository. In addition, on my weird Darwin system, I do =ln -sf
|
||
VCS/org-mode/lisp= and =ln -sf VCS/org-mode/testing= to fix the command =make
|
||
repro=.
|
||
|
||
#+caption[Batch Org testing]:
|
||
#+caption: Batch Org testing: obligatory before mailing a patch.
|
||
#+name: lst:batch-org-testing
|
||
#+begin_src shell -n :eval never :tangle no
|
||
make test > out.txt 2>&1
|
||
#+end_src
|
||
|
||
*** LaTex preamble editing using [[info:org#Noweb Reference Syntax][Noweb (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:file-inclusion-and-noweb
|
||
:END:
|
||
|
||
Listing [[lst:source-file-export-keyword-settings]] shows the preamble lines of this
|
||
[[file:README.org]] file. It lists the export keyword settings, the definition of
|
||
the [[https://orgmode.org/][Org mode]] =kbd= macro and the source block that generates part of the LaTeX
|
||
preamble. Listing [[lst:latex-header-1]], [[lst:latex-header-2]], [[lst:latex-header-3]],
|
||
[[lst:latex-header-4]], and [[lst:latex-header-5]] show the LaTeX source blocks that
|
||
exporting adds to the LaTeX preamble.
|
||
|
||
Note: [[info:org#Noweb Reference Syntax][Noweb (info)]] handles all LaTeX preamble listings in this section. For
|
||
this idea and more, see: [[https://list.orgmode.org/878rsar880.fsf@posteo.net/][LaTeX header blocks or managing lots of LaTeX headers]].
|
||
|
||
#+caption[Source file export keyword settings]:
|
||
#+caption: The preamble lines of README.org containing the export keyword
|
||
#+caption: settings, the definition of the Org-mode =kbd= macro, and the
|
||
#+caption: source block that generates part of the LaTeX preamble.
|
||
#+name: lst:source-file-export-keyword-settings
|
||
#+include: "README.org" src org -n :lines "1-17"
|
||
|
||
#+name: latex-header-1
|
||
#+begin_src latex -n :exports none
|
||
% Begin of the LaTeX preamble:
|
||
% See: https://list.orgmode.org/878rsar880.fsf@posteo.net/
|
||
% From: "Juan Manuel Macías" <maciaschain@posteo.net>
|
||
% To: William Denton <wtd@pobox.com>
|
||
% Cc: orgmode <emacs-orgmode@gnu.org>
|
||
% Subject: Re: #+latex_header blocks, or, managing lots of LaTeX headers
|
||
% Date: Tue, 12 Apr 2022 08:55:59 +0000 [thread overview]
|
||
% Message-ID: <878rsar880.fsf@posteo.net> (raw)
|
||
% LuaLaTeX-, PdfLaTeX-, or XeTeX-COMPILER COMPATIBILITY:
|
||
% Prevent collisions by using font packages before compiler specific packages.
|
||
\usepackage{ifthen,ifluatex,ifxetex}
|
||
\ifthenelse{\boolean{luatex}}{
|
||
\usepackage{fontspec} % lualatex
|
||
}{\ifthenelse{\boolean{xetex}}{
|
||
\usepackage{mathspec} % xetex
|
||
}{
|
||
\usepackage[T1]{fontenc} % pdflatex
|
||
\usepackage[utf8]{inputenc} % pdflatex
|
||
}
|
||
}
|
||
\usepackage{amsmath}
|
||
\usepackage{amssymb}
|
||
\usepackage{pifont} % check mark (\ding{52}) and cross mark (\ding{56})
|
||
\usepackage{textcomp} % \texttimes
|
||
\usepackage{wasysym} % \diameter
|
||
|
||
% Org-mode REQUIREMENTS:
|
||
\usepackage{graphicx}
|
||
\usepackage{longtable}
|
||
\usepackage{wrapfig}
|
||
\usepackage{rotating}
|
||
\usepackage[normalem]{ulem}
|
||
\usepackage{capt-of}
|
||
\usepackage{hyperref}
|
||
% End of the 1st LaTeX header block.
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: adding to the =Org-mode= default preamble]:
|
||
#+caption: LaTeX preamble: adding to the =Org-mode= default preamble.
|
||
#+name: lst:latex-header-1
|
||
#+begin_src latex -n :exports code :noweb yes
|
||
<<latex-header-1>>
|
||
#+end_src
|
||
|
||
#+name: latex-header-2
|
||
#+begin_src latex -n :exports none
|
||
% LANGUAGE:
|
||
\usepackage{babel}
|
||
\usepackage{fvextra}
|
||
\usepackage{csquotes}
|
||
|
||
% LISTS:
|
||
\usepackage{enumitem}
|
||
\setlist{noitemsep}
|
||
|
||
% LISTINGS:
|
||
% Section 2.6 of caption-eng.pdf (texdoc caption) explains that the sign
|
||
% of "skip" depends on the assumption "position=above" or "position=below".
|
||
% The assumption should match the real caption position in the LaTeX code.
|
||
\usepackage{caption}
|
||
\usepackage[newfloat]{minted}
|
||
\captionsetup[listing]{position=below,skip=0em}
|
||
\usemintedstyle{xcode}
|
||
|
||
% TABLES:
|
||
% https://tex.stackexchange.com/questions/341205/
|
||
% what-is-the-difference-between-tabular-tabular-and-tabularx-environments
|
||
% https://emacs.stackexchange.com/questions/26179/
|
||
% change-org-mode-table-style-just-for-latex-export
|
||
% https://tex.stackexchange.com/questions/468585/
|
||
% table-formatting-using-siunitx
|
||
\usepackage{booktabs}
|
||
\usepackage{colortbl}
|
||
\usepackage{tabularx} % DANGER: beware of Org table :width and :align options!
|
||
% End of the 2nd LaTeX header block.
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: language, lists and floats]:
|
||
#+caption: LaTeX preamble: language, lists and floats.
|
||
#+name: lst:latex-header-2
|
||
#+begin_src latex -n :exports code :noweb yes
|
||
<<latex-header-2>>
|
||
#+end_src
|
||
|
||
#+name: latex-header-3
|
||
#+begin_src latex -n :exports none
|
||
% PAGE LAYOUT:
|
||
\usepackage{fancyhdr}
|
||
\usepackage{lastpage}
|
||
\usepackage[
|
||
headheight=20mm,
|
||
top=40mm,
|
||
bottom=20mm,
|
||
left=0.1\paperwidth,
|
||
right=0.1\paperwidth,
|
||
heightrounded,
|
||
verbose,
|
||
]{geometry}
|
||
|
||
% TECHNICS:
|
||
\usepackage{siunitx}
|
||
\usepackage{tikz}
|
||
% End of the 3rd LaTeX header block.
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: page layout and technics]:
|
||
#+caption: LaTeX preamble: page layout.
|
||
#+name: lst:latex-header-3
|
||
#+begin_src latex -n :exports code :noweb yes
|
||
<<latex-header-3>>
|
||
#+end_src
|
||
|
||
#+name: latex-header-4
|
||
#+begin_src latex -n :exports none
|
||
% FLOAT BARRIERS:
|
||
% https://tex.stackexchange.com/questions/118662/use-placeins-for-subsections
|
||
% Make section an implicit float barrier:
|
||
\usepackage[section]{placeins}
|
||
% Make subsection an implicit float barrier:
|
||
\makeatletter
|
||
\AtBeginDocument{%
|
||
\expandafter\renewcommand\expandafter\subsection\expandafter{%
|
||
\expandafter\@fb@secFB\subsection
|
||
}%
|
||
}
|
||
\makeatother
|
||
% Make subsubsection an implicit float barrier:
|
||
\makeatletter
|
||
\AtBeginDocument{%
|
||
\expandafter\renewcommand\expandafter\subsubsection\expandafter{%
|
||
\expandafter\@fb@secFB\subsubsection
|
||
}%
|
||
}
|
||
% End of the 4th LaTeX header block.
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: float barriers]:
|
||
#+caption: LaTeX preamble: float barriers.
|
||
#+name: lst:latex-header-4
|
||
#+begin_src latex -n :exports code :noweb yes
|
||
<<latex-header-4>>
|
||
#+end_src
|
||
|
||
#+name: latex-header-5
|
||
#+begin_src latex -n :exports none
|
||
% FANCY HEADERS AND FOOTERS:
|
||
% Add fancy headers and footers to normal pages.
|
||
\pagestyle{fancy}
|
||
\fancyhf{}
|
||
\renewcommand{\footrulewidth}{0.4pt}
|
||
\fancyfoot[C]{\emph{
|
||
Emacs for \LaTeX{}, Lisp, Org, and Python -- Gerard Vermeulen
|
||
}
|
||
}
|
||
\renewcommand{\headrulewidth}{0.4pt}
|
||
\fancyhead[L]{\includegraphics[height=1.8cm]{Org-mode-unicorn.png}}
|
||
\fancyhead[C]{
|
||
Page: \thepage/\pageref{LastPage} \\
|
||
\text{ } \\
|
||
\text{ } \\
|
||
DRAFT
|
||
}
|
||
\fancyhead[R]{\includegraphics[height=1.8cm]{Emacs-logo.png}}
|
||
|
||
% Add fancy header and footer to custom titlepage.
|
||
% https://tex.stackexchange.com/questions/506102/
|
||
% adding-header-and-footer-to-custom-titlepage
|
||
\fancypagestyle{titlepage}{%
|
||
\fancyhf{}
|
||
\renewcommand{\footrulewidth}{0.4pt}
|
||
\fancyfoot[C]{\emph{
|
||
Emacs for \LaTeX{}, Lisp, Org, and Python -- Gerard Vermeulen
|
||
}
|
||
}
|
||
\renewcommand{\headrulewidth}{0.4pt}
|
||
\fancyhead[L]{\includegraphics[height=1.8cm]{Org-mode-unicorn.png}}
|
||
\fancyhead[C]{
|
||
\pageref{LastPage} pages \\
|
||
\text{ } \\
|
||
\text{ } \\
|
||
DRAFT
|
||
}
|
||
\fancyhead[R]{\includegraphics[height=1.8cm]{Emacs-logo.png}}
|
||
}
|
||
% End of the 5th and last LaTeX header block.
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: fancy headers and footers]:
|
||
#+caption: LaTeX preamble: fancy headers and footers.
|
||
#+name: lst:latex-header-5
|
||
#+begin_src latex -n :exports code :noweb yes
|
||
<<latex-header-5>>
|
||
#+end_src
|
||
|
||
*** [[info:org#LaTeX header and sectioning][Deprecated LaTeX preamble editing methods]] :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:easy-latex-preamble-editing
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
*NOTE:* This [[info:org#Top][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 [[info:org#LaTeX header and sectioning][LaTeX header and sectioning (info)]].
|
||
|
||
The old way -- exploiting an idea of [[https://www.matem.unam.mx/~omar/][Omar Antolin Camarena]] -- is to code new
|
||
[[info:org#Editing Source Code][<LANGUAGE>-modes]] allowing to edit in LaTeX mode and to export to LaTeX code with
|
||
[[info:org#LaTeX specific export settings][correct LaTeX preamble export setting prefixes]]. Here, are links to three posts
|
||
exposing his idea:
|
||
1. [[https://www.reddit.com/r/orgmode/comments/7u2n0h/tip_for_defining_latex_macros_for_use_in_both/][Export LaTeX macros to LaTeX and HTML/MathJax preambles (reddit)]],
|
||
2. [[https://www.reddit.com/r/orgmode/comments/5bi6ku/tip_for_exporting_javascript_source_block_to/][Export JavaScript source blocks to script tags in HTML (reddit)]],
|
||
3. [[https://emacs.stackexchange.com/questions/28301/export-javascript-source-block-to-script-tag-in-html-when-exporting-org-file-to][Export JavaScript source blocks to script tags in HTML (SX)]].
|
||
Listing [[lst:org-babel-latex-header-blocks]] implements this way by means of two
|
||
new [[info:org#Editing Source Code][<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 [[https://git.sr.ht/~bzg/org-contrib/tree/master/item/lisp/ox-extra.el][ox-extra.el]]. Apparently, nobody is using
|
||
this broken function (broken, since it relies on support only in org-mode before
|
||
=2014-11-11=). Listing [[lst:org-latex-header-blocks-filter]] proposes a fix for
|
||
=org-latex-header-blocks-filter=. 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 [[info:org#Export Settings][#+SETUPFILE: <FILE>]], but only
|
||
in [[info:org#Export Settings][#+INCLUDE: <FILE>]] files.
|
||
|
||
#+caption[New =<LANGUAGE>-modes= to edit the LaTeX preamble easily]:
|
||
#+caption: Add =latex-header= and =latex-extra-header= language modes to edit
|
||
#+caption: LaTeX preamble =latex_header= and =latex_extra_header= export options
|
||
#+caption: easily.
|
||
#+name: lst:org-babel-latex-header-blocks
|
||
#+begin_src emacs-lisp -n :exports code :results silent
|
||
(with-eval-after-load 'emacs
|
||
(defun prefix-all-lines (prefix body)
|
||
(with-temp-buffer
|
||
(insert body)
|
||
(string-insert-rectangle (point-min) (point-max) prefix)
|
||
(buffer-string)))
|
||
|
||
(defun org-babel-execute:latex-extra-header (body _params)
|
||
"Execute a block of LaTeX extra header lines with org-babel.
|
||
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
|
||
(add-to-list 'org-src-lang-modes '("latex-header" . latex))
|
||
(add-to-list 'org-src-lang-modes '("latex-extra-header" . latex))))
|
||
#+end_src
|
||
|
||
#+caption[Convert marked LaTeX export blocks to LaTeX header lines]:
|
||
#+caption: Convert marked LaTeX export blocks to LaTeX header lines.
|
||
#+name: lst:org-latex-header-blocks-filter
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'ox
|
||
(defun org-latex-header-blocks-filter (backend)
|
||
"Convert marked LaTeX export blocks to \"#+latex_header: \" lines.
|
||
The marker is a line \"#+header: :header yes\" preceding the block.
|
||
|
||
For instance, the LaTeX export block
|
||
|
||
,#+header: :header yes
|
||
,#+begin_export latex
|
||
% This line converts to a LaTeX header line.
|
||
,#+end_export
|
||
|
||
converts to
|
||
|
||
\"#+latex_header: % This line converts to a LaTeX header line.\"."
|
||
(when (org-export-derived-backend-p backend 'latex)
|
||
(let ((blocks
|
||
(org-element-map
|
||
(org-element-parse-buffer 'greater-element nil) 'export-block
|
||
(lambda (block)
|
||
(let ((type (org-element-property :type block))
|
||
(header (org-export-read-attribute :header block :header)))
|
||
(when (and (string= type "LATEX") (string= header "yes"))
|
||
block))))))
|
||
(mapc (lambda (block)
|
||
;; Set point to where to insert LaTeX header lines
|
||
;; after deleting the block.
|
||
(goto-char (org-element-property :post-affiliated block))
|
||
(let ((lines
|
||
(split-string (org-element-property :value block) "\n")))
|
||
(delete-region (org-element-property :begin block)
|
||
(org-element-property :end block))
|
||
(dolist (line lines)
|
||
(insert (concat "#+latex_header: "
|
||
(replace-regexp-in-string "\\` *" "" line)
|
||
"\n")))))
|
||
;; Reverse to go upwards to avoid wrecking the list of
|
||
;; block positions in the file that would occur in case
|
||
;; of going downwards.
|
||
(reverse blocks)))))
|
||
|
||
;; Push the filter on the hook.
|
||
(cl-pushnew #'org-latex-header-blocks-filter
|
||
org-export-before-parsing-functions))
|
||
#+end_src
|
||
|
||
*** [[info:org#LaTeX specific export settings][Advanced LaTeX export settings]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:advanced-latex-export-settings
|
||
:END:
|
||
|
||
Listing [[lst:ox-latex-emacs-lisp-setup]] initializes the buffer local variables
|
||
=org-latex-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.
|
||
|
||
#+caption[Define buffer local =ox-latex= variables]:
|
||
#+caption: Define buffer local =ox-latex= variables.
|
||
#+name: lst:ox-latex-emacs-lisp-setup
|
||
#+begin_src emacs-lisp -n :exports code :results none :tangle no
|
||
(when (require 'ox-latex nil 'noerror)
|
||
;; https://emacs.stackexchange.com/questions/47347/
|
||
;; customizing-org-latex-title-command-to-edit-title-page
|
||
;; https://tex.stackexchange.com/questions/506102/
|
||
;; adding-header-and-footer-to-custom-titlepage
|
||
(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 ""))
|
||
#+end_src
|
||
|
||
*** [[info:org#HTML Export][HTML export (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:html-export
|
||
:END:
|
||
|
||
The code label and link feature in [[info:Org#Literal Examples][Literal Examples (info)]] does not integrate
|
||
with LaTeX export.
|
||
|
||
#+caption[Set HTML export options]:
|
||
#+caption: Set HTML export options.
|
||
#+name: lst:set-html-export-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'ox-html
|
||
;; (Info-find-node "Org" "Literal Examples")
|
||
(setopt org-html-head-include-scripts t))
|
||
#+end_src
|
||
|
||
*** [[https://orgmode.org/worg/dev/org-syntax-edited.html][Org Syntax]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:org-syntax
|
||
:END:
|
||
|
||
Two tools to grok how [[https://orgmode.org/worg/dev/org-element-api.html][Org mode parsing]] works are the [[https://orgmode.org/worg/dev/org-syntax-edited.html][Org Syntax]] specification
|
||
and the [[http://xahlee.info/emacs/emacs/elisp_parse_org_mode.html][Org mode parser tutorial]]. The [[https://orgmode.org/worg/dev/org-element-api.html][Org element parsing API]] boils down to three
|
||
functions:
|
||
1. The function [[https://orgmode.org/worg/dev/org-element-api.html#global][~org-element-parse-buffer~]] implements a fully recursive buffer
|
||
parser that returns an abstract syntax tree.
|
||
2. The functions [[https://orgmode.org/worg/dev/org-element-api.html#local][~org-element-at-point~ and ~org-element-context~]] return
|
||
information on the document structure around point either at the element
|
||
level or at the object level in case of ~org-element-context~.
|
||
Listing [[lst:grok-org-element-tree]] improves the [[http://xahlee.info/emacs/emacs/elisp_parse_org_mode.html][Org mode parser tutorial]] by
|
||
defining interactive wrappers that pretty print the results of those
|
||
non-interactive =org-element= functions to an =Emacs-lisp= buffer.
|
||
|
||
#+caption[Grok how =org-element= parses your document]:
|
||
#+caption: Grok how =org-element= parses your document.
|
||
#+name: lst:grok-org-element-tree
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'org-element
|
||
(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)))
|
||
#+end_src
|
||
|
||
** Grammar, spelling, and style tools
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-tools
|
||
:END:
|
||
|
||
*** [[info:emacs#Abbrevs][Abbrevs (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-abbreviations
|
||
:END:
|
||
|
||
[[https://www.masteringemacs.org/][Mickey Peterson]] has posted [[https://www.masteringemacs.org/article/correcting-typos-misspellings-abbrev][Correcting Typos and Misspellings with Abbrev]] showing
|
||
how to use [[info:emacs#Keyboard Macros][Keyboard Macros (info)]] to exploit [[https://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines][Wikipedia's list of common
|
||
misspellings for machines]]. Listing [[lst:misspellings-abbrev]] defines his keyboard
|
||
macro under the name =misspellings-abrev=. I have used those directions to
|
||
define src_emacs-lisp[:results silent]{(describe-variable 'global-abbrev-table)}
|
||
and to store it in src_emacs-lisp[:results none]{(describe-variable
|
||
'abbrev-file-name)} to manage its changes with git. I can change the
|
||
abbreviation definitions in this file by means of:
|
||
1. Execute src_emacs-lisp[:results silent]{(edit-abbrevs)} to alter abbreviation
|
||
definitions by editing an =*Abbrevs*= buffer.
|
||
2. Add, edit, or remove definitions of the form ="source" 1 "target"= under the
|
||
global or a mode-specific table.
|
||
3. Execute src_emacs-lisp{(abbrev-edit-save-buffer)} to save all user
|
||
abbreviation definitions in the current buffer.
|
||
|
||
#+caption[Definition of the =misspellings-abbrev= keyboard macro]:
|
||
#+caption: Definition of the =misspellings-abbrev= keyboard macro.
|
||
#+name: lst:misspellings-abbrev
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'emacs
|
||
(defun browse-common-misspellings ()
|
||
"Open the Wikipedia page of common misspellings for machines in EWW."
|
||
(interactive)
|
||
(eww (concat "https://en.wikipedia.org/wiki/Wikipedia"
|
||
":Lists_of_common_misspellings/For_machines")))
|
||
|
||
(fset 'misspellings-abbrev
|
||
(kmacro-lambda-form
|
||
[?\C-s ?- ?> return backspace backspace ?\C-k ?\C-x ?a ?i ?g ?\C-y return]
|
||
0 "%d"))
|
||
|
||
(setq-default abbrev-mode t))
|
||
#+end_src
|
||
|
||
*** TODO [[https://dict.org/bin/Dict][DICT.org]] local server fails on Darwin
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-dict
|
||
:END:
|
||
|
||
Evaluating src_emacs-lisp{(dictionary)} connects to a local or remote =dictd=
|
||
server. The following links explain how to configure and use =dictd=:
|
||
1. [[https://jpmens.net/2020/03/08/looking-up-words-with-dict/][Looking up words with DICT]]
|
||
2. [[https://www.rfc-editor.org/rfc/rfc2229.html][RFC 2229: A Dictionary Server Protocol]]
|
||
3. [[https://www.masteringemacs.org/article/wordsmithing-in-emacs][Wordsmithing in Emacs]]
|
||
|
||
I am using the following Debian Bullseye dictionaries on Darwin and Gentoo:
|
||
1. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-devil/dict-devil_1.0-13.1_all.deb][dict-devil_1.0-13.1_all.deb]]
|
||
2. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-foldoc/dict-foldoc_20201018-1_all.deb][dict-foldoc_20201018-1_all.deb]]
|
||
3. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-gcide/dict-gcide_0.48.5+nmu1_all.deb][dict-gcide_0.48.5+nmu1_all.deb]]
|
||
4. [[http://ftp.fr.debian.org/debian/pool/main/d/dict-jargon/dict-jargon_4.4.7-3.1_all.deb][dict-jargon_4.4.7-3.1_all.deb]]
|
||
5. [[http://ftp.fr.debian.org/debian/pool/main/v/vera/dict-vera_1.24-1_all.deb][dict-vera_1.24-1_all.deb]]
|
||
6. [[http://ftp.fr.debian.org/debian/pool/main/w/wordnet/dict-wn_3.0-36_all.deb][dict-wn_3.0-36_all.deb]]
|
||
|
||
#+caption[Resource file for =dict= on =Darwin=]:
|
||
#+caption: Resource file for =dict= on =Darwin=.
|
||
#+name: lst:darwin-dict-resource-file
|
||
#+header: :tangle (if (eq 'darwin system-type) "~/.dictrc" "no")
|
||
#+begin_src conf -n
|
||
# https://jpmens.net/2020/03/08/looking-up-words-with-dict/
|
||
server 127.0.0.1 {
|
||
port 2628
|
||
}
|
||
# Local Variables:
|
||
# mode: conf-unix
|
||
# End:
|
||
#+end_src
|
||
|
||
#+caption[Make a configuration file for =dictd= on =Darwin=]:
|
||
#+caption: Make a configuration file for =dictd= on =Darwin=.
|
||
#+name: lst:dictd-configuration-file
|
||
#+begin_src shell -n :eval (if (eq 'darwin system-type) "yes" "never") :results silent
|
||
cat > ~/.dictd.conf <<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
|
||
#+end_src
|
||
|
||
#+caption[Run =dictd= in debug mode on =Darwin=]:
|
||
#+caption: Run =dictd= in debug mode on =Darwin=.
|
||
#+name: lst:darwin-dictd-launch
|
||
#+begin_src shell -n :eval never :tangle no
|
||
# https://jpmens.net/2020/03/08/looking-up-words-with-dict/
|
||
/usr/local/sbin/dictd \
|
||
--config .dictd.conf \
|
||
--verbose \
|
||
--logfile .dictd.log \
|
||
-d nodetach
|
||
#+end_src
|
||
|
||
*** [[https://github.com/bnbeckwith/writegood-mode#readme][Writegood mode]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-writegood-mode
|
||
:END:
|
||
|
||
[[https://github.com/bnbeckwith/writegood-mode#readme][Writegood mode]] is a minor mode to aid in finding common writing problems. The
|
||
post [[http://matt.might.net/articles/shell-scripts-for-passive-voice-weasel-words-duplicates/][Matt Might's "My Ph.D. advisor rewrote himself in bash" scripts]] inspired
|
||
this mode. Listing [[lst:configure-writegood-mode]] configures [[https://github.com/bnbeckwith/writegood-mode#readme][writegood mode]] and
|
||
enables =writegood-mode= for =text-mode= buffers. Therefore, the best way use
|
||
it for this buffer is by typing {{{kbd(C-c C-e t U)}}} to export the it to a
|
||
=text-mode= buffer where the =writegood-mode= faces are more visible.
|
||
|
||
#+caption[Configure =writegood-mode=]:
|
||
#+caption: Configure =writegood-mode=.
|
||
#+name: lst:configure-writegood-mode
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation '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))
|
||
#+end_src
|
||
|
||
** [[info:emacs#Which Function][Which-function-mode (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:which-function-mode
|
||
:END:
|
||
|
||
Listing [[lst:setup-which-function-mode]] sets ~which-function-mode~ options. The
|
||
post [[https://codelearn.me/2024/02/02/emacs-which-function-mode.html][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:
|
||
- [[https://list.orgmode.org/20240205.141235.268481480563517065.teika@gmx.com/][Show current org-mode headline in frame header]].
|
||
- [[https://emacs.stackexchange.com/questions/30894/][Show current org-mode headline in modeline]].
|
||
|
||
#+caption[Setup ~which-function-mode~]:
|
||
#+caption: Setup ~which-function-mode~.
|
||
#+name: lst:setup-which-function-mode
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'which-func
|
||
(setopt which-func-modes '(emacs-lisp-mode org-mode pdf-view-mode)))
|
||
#+end_src
|
||
|
||
#+caption[Define functions for ~which-func-functions~]:
|
||
#+caption: Define functions for ~which-func-functions~.
|
||
#+name: lst:define-for-which-func-functions
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; 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)
|
||
#+end_src
|
||
|
||
* Programming Tools
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:programming-tools
|
||
:END:
|
||
|
||
** [[info:eglot#Top][Eglot (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:eglot
|
||
:END:
|
||
|
||
[[https://github.com/joaotavora/eglot#readme][Emacs polyGLOT (Eglot)]] is an Emacs language-server-protocol client that stays
|
||
out of your way. [[info:eglot#Top][Eglot (info)]] is a builtin since Emacs-29.1. The following
|
||
listings set up [[https://github.com/joaotavora/eglot][Eglot]]:
|
||
1. Listing [[lst:minimal-eglot-setup][minimal Eglot setup]] adds key bindings to =eglot-mode-keymap= and
|
||
ensures automatic [[https://github.com/joaotavora/eglot][Eglot]] startup in case of visiting files in =python-mode=.
|
||
2. Listing [[lst:setup-oglot]] uses [[https://github.com/gav451/oglot#][oglot]] to enable using [[https://github.com/joaotavora/eglot][Eglot]] in =org-src-mode=
|
||
Python buffers.
|
||
|
||
#+caption[Ensure =eglot= installation with minimal setup]:
|
||
#+caption: Ensure =eglot= installation with minimal setup.
|
||
#+name: lst:minimal-eglot-setup
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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))
|
||
|
||
(add-hook 'python-mode-hook #'eglot-ensure)
|
||
#+end_src
|
||
|
||
#+caption: Setup ~oglot~ for ~python-mode~.
|
||
#+name: lst:setup-oglot
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(unless (package-installed-p 'oglot)
|
||
(package-vc-install '(oglot :url "https://github.com/gav451/oglot.git")))
|
||
(when (require 'oglot nil 'noerror)
|
||
(setopt oglot-maybe-ensure-modes '(python-mode)))
|
||
#+end_src
|
||
|
||
** [[https://github.com/lassik/emacs-format-all-the-code#readme][Format-all]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:format-all
|
||
:END:
|
||
|
||
Listing [[lst:configure-format-all]]:
|
||
1. Configures [[https://github.com/lassik/emacs-format-all-the-code#readme][format-all]] which is a package that provides an universal interface
|
||
to code formatters of more than 60 computer languages.
|
||
2. Adds =format-all-org-babel-post-tangle= to =org-babel-post-tangle-hook= to
|
||
format tangled Python code.
|
||
|
||
#+caption[Configure =format-all=]:
|
||
#+caption: Configure =format-all=.
|
||
#+name: lst:configure-format-all
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; 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)))))))
|
||
#+end_src
|
||
|
||
** [[info:flymake#Top][Flymake (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:flymake
|
||
:END:
|
||
|
||
Flymake is an universal on-the-fly syntax checker for Emacs. It is a requirement
|
||
of [[https://github.com/joaotavora/eglot][eglot]], but you can use it without [[https://github.com/joaotavora/eglot][eglot]] for instance in [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][python-mode]] buffers
|
||
that do not visit a file. Listing [[lst:configure-flymake]] aliases =list-errors=
|
||
(new) to =flymake-show-buffer-diagnostics= (old).
|
||
|
||
#+caption[Configure =Flymake=]:
|
||
#+caption: Configure =Flymake=
|
||
#+name: lst:configure-flymake
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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))
|
||
#+end_src
|
||
|
||
* Programming Modes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:programming-languages
|
||
:END:
|
||
|
||
** [[https://dept-info.labri.fr/~strandh/Teaching/PFS/Common/Strandh-Tutorial/Dir-symbolic.html][Common Lisp programming]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:common-lisp-programming
|
||
:END:
|
||
|
||
Links to Common Lisp books and posts are:
|
||
1. [[https://lisp-lang.org/][Common Lisp]]
|
||
2. [[https://kvardek-du.kerno.org/][Kvardek Du - Luis Oliveira]]
|
||
3. [[https://redirect.cs.umbc.edu/courses/331/resources/lisp/][Lisp Resources]]
|
||
4. [[https://redirect.cs.umbc.edu/courses/331/resources/lisp/onLisp/][On Lisp - Paul Graham]]
|
||
5. [[https://dept-info.labri.fr/~strandh/Teaching/Programmation-Symbolique/Common/Book/HTML/programmation.html][Traité de Programmation en Common Lisp]]
|
||
|
||
*** [[https://joaotavora.github.io/sly/][Sly]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:sly
|
||
:END:
|
||
|
||
Listing [[lst:setup-sly]] sets up the [[info:sly#Top][Sly (info)]] Common Lisp IDE for Emacs for use
|
||
with [[http://www.sbcl.org/][Steel Bank Common Lisp (sbcl)]]:
|
||
1. It sets the =sly-default-lisp= and =sly-lisp-implementations= options. It
|
||
uses a core image file to apply the technique to [[info:sly#Loading Slynk faster][load Slynk faster (info)]] and
|
||
listing [[lst:sbcl-core-for-sly]] tangles to a script to dump such a [[http://www.sbcl.org/][SBCL]] core.
|
||
2. It does not ensure [[info:sly#Auto-SLY][Connecting to SLY automatically (info)]] when opening a
|
||
Common Lisp file because that does not play well with =ob-lisp=.
|
||
3. It configures searching documentation in the [[http://www.lispworks.com/documentation/HyperSpec/Front/][Common Lisp HyperSpec]] according
|
||
to [[info:sly#Basic customization][Basic customization (info)]].
|
||
|
||
#+caption[Setup =sly=]:
|
||
#+caption: Setup =sly=.
|
||
#+name: lst:setup-sly
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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-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")))))
|
||
(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)))
|
||
#+end_src
|
||
|
||
#+caption[Script to dump a SBCL core for the Sly Common Lisp IDE]:
|
||
#+caption: Script to dump a SBCL core for the Sly Common Lisp IDE.
|
||
#+header: :tangle-mode (identity #o755)
|
||
#+name: lst:sbcl-core-for-sly
|
||
#+begin_src shell -n :eval never :tangle ~/bin/sbcl.core-for-sly
|
||
#!/bin/sh # -*- shell-script -*-
|
||
sbcl <<EOF
|
||
(mapc 'require '(sb-bsd-sockets sb-posix sb-introspect sb-cltl2 asdf))
|
||
(save-lisp-and-die "sbcl.core-for-sly")
|
||
EOF
|
||
# sbcl.core-for-sly ends here
|
||
#+end_src
|
||
|
||
*** [[info:sly#Other configurables][Slynk configurables (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:slynk
|
||
:END:
|
||
|
||
Listing [[lst:slink-sel]] allows to set or get the Sly
|
||
=slynk:*string-elision-length*=.
|
||
|
||
#+caption[Get/set Sly =slynk:*string-elision-length*= functionality]:
|
||
#+caption: Get/set Sly =slynk:*string-elision-length*= functionality.
|
||
#+name: lst:slink-sel
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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))))
|
||
#+end_src
|
||
|
||
*** [[https://courses.cs.northwestern.edu/325/][CS 325 AI Programming]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:cs-325-ai-programming
|
||
:END:
|
||
|
||
The [[https://courses.cs.northwestern.edu/325/][CS 325 AI Programming]] course allows to learn Common Lisp by self-study and
|
||
the page [[https://courses.cs.northwestern.edu/325/admin/lisp-setup.php][CS 325: Setting up Lisp]] gives instructions how to:
|
||
1. [[https://courses.cs.northwestern.edu/325/admin/lisp-setup.php#lisp][Download and install common lisp]].
|
||
2. [[https://courses.cs.northwestern.edu/325/admin/lisp-setup.php#quicklisp][Install Quicklisp]].
|
||
3. [[https://courses.cs.northwestern.edu/325/admin/lisp-setup.php#install-325][Install the CS325 library]].
|
||
|
||
*** [[https://www.quicklisp.org/][Quicklisp]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:quicklisp
|
||
:END:
|
||
|
||
[[https://www.quicklisp.org/][Quicklisp]] is a library manager for Common Lisp. Listing
|
||
[[lst:download+verify-quicklisp]] downloads the [[https://www.quicklisp.org/][Quicklisp]] installation file and
|
||
verifies its signature to prevent tampering. Listing [[lst:bootstrap-quicklisp]]
|
||
tangles to a shell script allowing to bootstrap [[https://www.quicklisp.org/][Quicklisp]] with [[http://www.sbcl.org/][SBCL]]. Listing
|
||
[[lst:quicklisp-sbclrc-file]] tangles to the a [[http://www.sbcl.org/][SBCL]] resource file with [[https://www.quicklisp.org/][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.
|
||
|
||
#+caption[Download and verify =quicklisp=]:
|
||
#+caption: Download and verify =quicklisp=.
|
||
#+name: lst:download+verify-quicklisp
|
||
#+begin_src shell -n :dir ~ :results none :tangle no
|
||
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
|
||
#+end_src
|
||
|
||
#+caption[Bootstrap =quicklisp=]:
|
||
#+caption: Bootstrap =quicklisp=.
|
||
#+header: :tangle-mode (identity #o755)
|
||
#+name: lst:bootstrap-quicklisp
|
||
#+begin_src shell -n :eval never :tangle ~/bin/quicklisp-sbcl-bootstrap
|
||
#!/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:
|
||
#+end_src
|
||
|
||
#+caption[Clone local =quicklisp= projects]:
|
||
#+caption: Clone local =quicklisp= projects.
|
||
#+name: lst:clone-local-projects
|
||
#+begin_src shell -n :dir ~/quicklisp/local-projects :results none :tangle no
|
||
git clone https://gitlab.com/criesbeck/cs325.git
|
||
git clone git@github.com:ageldama/cl-state-machine.git
|
||
#+end_src
|
||
|
||
#+caption[Register and load local =quicklisp= projects with dependencies]:
|
||
#+caption: Register and load local =quicklisp= projects with dependencies.
|
||
#+name: lst:register-load-local-projects
|
||
#+begin_src lisp -n :eval never-export :results none :tangle no
|
||
;; 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")))
|
||
#+end_src
|
||
|
||
#+caption: A =quicklisp= sbclrc file.
|
||
#+name: lst:quicklisp-sbclrc-file
|
||
#+begin_src lisp -n :eval never :tangle ~/.sbclrc
|
||
;;; 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)))
|
||
#+end_src
|
||
|
||
** [[https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/][Lisp mode custom font locking for Common Lisp]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:lisp-custom-font-locking
|
||
:END:
|
||
|
||
This section implements the code described in the [[https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/][Emacs Common Lisp Font Locking]]
|
||
post.
|
||
|
||
*** [[https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/][Initialize Common Lisp custom font locking]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:cl-custom-font-locking-start
|
||
:END:
|
||
|
||
#+caption[Define faces for =Common Lisp= custom font locking]:
|
||
#+caption: Define faces for =Common Lisp= custom-font-locking.
|
||
#+name: lst:cl-custom-font-locking-faces
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;;; 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.")
|
||
#+end_src
|
||
|
||
#+caption[Define a =Common Lisp= function to find standard symbol names]:
|
||
#+caption: Define a =Common Lisp= function to find standard symbol names.
|
||
#+name: lst:standard-symbol-names
|
||
#+begin_src lisp -n :eval never-export :results silent :tangle no
|
||
(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<)))
|
||
#+end_src
|
||
|
||
#+caption[Set Sly ~slynk:*string-elision-length*~]:
|
||
#+caption: Set Sly ~slynk:*string-elision-length*~.
|
||
#+name: lst:set-sly-sel
|
||
#+header: :wrap "src text -n"
|
||
#+begin_src emacs-lisp -n :eval never-export :exports both :tangle no
|
||
(when (and (fboundp 'sly-connected-p) (sly-connected-p))
|
||
(and (fboundp 'slynk-set-sel) (slynk-set-sel (ash 1 14))))
|
||
#+end_src
|
||
|
||
#+caption[Set Sly ~slynk:*string-elision-length*~ result]:
|
||
#+caption: Set Sly ~slynk:*string-elision-length*~ result.
|
||
#+name: lst:set-sly-sel-result
|
||
#+RESULTS: lst:set-sly-sel
|
||
#+begin_src text -n
|
||
16384 (15 bits, #x4000)
|
||
#+end_src
|
||
|
||
*** [[https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/][Collect standard symbol function names]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:cl-function-names
|
||
:END:
|
||
|
||
#+caption[Define the =cl-function-names= variable in an =Emacs Lisp= block]:
|
||
#+caption: Use =Common Lisp= to define the =cl-function-names= variable in
|
||
#+caption: an =Emacs Lisp= source block for tangling into =user-init-file=.
|
||
#+name: lst:define-cl-function-names
|
||
#+header: :wrap "src emacs-lisp -n :results silent"
|
||
#+begin_src lisp -n :exports both :eval never-export
|
||
(format nil "(defvar~% cl-function-names~% '~S)~%"
|
||
(standard-symbol-names #'fboundp))
|
||
#+end_src
|
||
|
||
#+RESULTS: lst:define-cl-function-names
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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"))
|
||
#+end_src
|
||
|
||
*** [[https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/][Collect standard symbol value names]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:cl-value-names
|
||
:END:
|
||
|
||
#+caption[Define the =cl-variable-names= variable in an =Emacs Lisp= block]:
|
||
#+caption: Use =Common Lisp= to define the =cl-variable-names= variable in
|
||
#+caption: an =Emacs Lisp= source block for tangling into =user-init-file=.
|
||
#+name: lst:define-cl-value-names
|
||
#+header: :wrap "src emacs-lisp -n :results silent"
|
||
#+begin_src lisp -n :exports both :eval never-export
|
||
(format nil "(defvar~% cl-value-names~% '~S)~%"
|
||
(standard-symbol-names #'boundp))
|
||
#+end_src
|
||
|
||
#+RESULTS: lst:define-cl-value-names
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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"))
|
||
#+end_src
|
||
|
||
*** [[https://www.n16f.net/blog/custom-font-lock-configuration-in-emacs/][Finalize Common Lisp custom font locking]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:cl-custom-font-locking-final
|
||
:END:
|
||
|
||
#+caption[Finalize =Common Lisp= custom font locking]:
|
||
#+caption: Finalize =Common Lisp= custom font locking.
|
||
#+name: lst:finalize-cl-custom-font-locking
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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)
|
||
#+end_src
|
||
|
||
** [[info:eintr#Top][Emacs Lisp Programming (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-lisp-programming
|
||
:END:
|
||
|
||
Here is a list of links describing how to program and debug [[info:elisp#Top][Emacs Lisp (info)]] code:
|
||
1. [[https://www.masteringemacs.org/article/evaluating-elisp-emacs][Evaluating Elisp in Emacs]]
|
||
2. [[https://endlessparentheses.com/debugging-emacs-lisp-part-1-earn-your-independence.html][Debugging Elisp Part 1: Earn your independence]]
|
||
3. [[https://endlessparentheses.com/debugging-elisp-part-2-advanced-topics.html][Debugging Elisp Part 2: Advanced topics]]
|
||
4. [[http://xahlee.info/talk_show/xah_talk_show_2022-01-20.html][Xah talk show: Elisp coding: xah-add-space-after-comma]]
|
||
5. [[http://xahlee.info/talk_show/xah_talk_show_2022-01-22.html][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.
|
||
|
||
*** [[https://www.masteringemacs.org/article/evaluating-elisp-emacs][Evaluating Elisp in Emacs]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:elisp-evaluation
|
||
:END:
|
||
|
||
Listing [[lst:setup-ielm][setup ielm]] configures the [[https://wikemacs.org/wiki/IELM][Interactive Emacs Lisp Mode]] for better
|
||
interoperability with [[https://smartparens.readthedocs.io/en/latest/][smartparens]]: get help on the key bindings by means of
|
||
src_emacs-lisp[:results none]{(describe-function 'ielm)}.
|
||
|
||
#+caption[Setup =ielm=]:
|
||
#+caption: Setup =ielm=.
|
||
#+name: lst:setup-ielm
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'ielm
|
||
(setopt ielm-dynamic-return nil))
|
||
#+end_src
|
||
|
||
*** [[info:elisp#Debugging][Debugging Emacs Lisp (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:debugging
|
||
:END:
|
||
|
||
GAV: I fail to instrument the functions from the [[https://www.emacswiki.org/emacs/SourceLevelDebugger][source level debugger (edebug)]]
|
||
wiki entry as the [[info:elisp#Edebug][Edebug (info)]] manual explains.
|
||
|
||
*** [[info:emacs#Known Problems][Reading existing bug reports (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:bug-reports
|
||
:END:
|
||
|
||
Listing [[lst:bug-reports]] ensures the installation of the [[info:debbugs#Top][Debbugs (info)]] extension
|
||
package to access the [[https://debbugs.gnu.org/][GNU Bug Tracker]], sets up [[info:emacs#Bug Reference][Bug Reference Mode (info)]] to
|
||
access the [[https://lists.gnu.org/mailman/listinfo/bug-gnu-emacs][bug-gnu-emacs]] mailing list, and enables [[info:emacs#Goto Address mode][Goto Address mode (info)]] to
|
||
open web links in source blocks.
|
||
|
||
#+caption[Setup reading existing bug reports]:
|
||
#+caption: Setup reading existing bug reports.
|
||
#+name: lst:bug-reports
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(ensure-package-installation 'debbugs)
|
||
|
||
(defvar bug-reference-url-format
|
||
"https://debbugs.gnu.org/cgi/bugreport.cgi?bug=%s"
|
||
"Format to use `gnu-debbugs' URL.")
|
||
|
||
(add-to-list 'safe-local-eval-forms '(bug-reference-mode +1))
|
||
(put 'bug-reference-mode 'safe-local-variable 'booleanp)
|
||
|
||
(add-hook 'org-mode-hook #'goto-address-mode)
|
||
#+end_src
|
||
|
||
*** [[http://yummymelon.com/devnull/writing-better-elisp-docstrings.html][Writing Better Elisp Docstrings]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:describe-function-at-point
|
||
:END:
|
||
|
||
#+caption[Describe Elisp function at point]:
|
||
#+caption: Describe Elisp function at point.
|
||
#+name: lst:describe-function-at-point
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; Stolen from an old version of a Charles Choi post:
|
||
;; 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.
|
||
|
||
Works in `emacs-lisp-mode', `text-mode', and also `org-mode' after
|
||
switching 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
|
||
(keymap-set org-mode-map "C-c d" #'describe-function-at-point))
|
||
(with-eval-after-load 'elisp-mode
|
||
(keymap-set emacs-lisp-mode-map "C-c d" #'describe-function-at-point))
|
||
#+end_src
|
||
|
||
** [[https://go.dev/][Go Programming]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:go-programming
|
||
:END:
|
||
|
||
Links for further investigation are:
|
||
1. [[https://gobyexample.com/][Go by example]] is a showcase of short examples with explanations.
|
||
|
||
#+caption[Setup Go programming]:
|
||
#+caption: Setup Go programming.
|
||
#+name: lst:setup-go
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (featurep 'treesit)
|
||
(setopt go-ts-mode-indent-offset 2)
|
||
(add-hook 'go-ts-mode-hook (lambda ()
|
||
(setq-local tab-width 2)))
|
||
(add-to-list 'auto-mode-alist `(,(rx ".go" eos) . go-ts-mode)))
|
||
#+end_src
|
||
|
||
** [[https://www.seas.upenn.edu/~chaoliu/2017/09/01/python-programming-in-emacs/][Python programming]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:python-programming
|
||
:END:
|
||
|
||
The [[https://www.emacswiki.org/emacs/PythonProgrammingInEmacs][Python Programming in Emacs]] wiki page lists options to enhance Emacs's
|
||
built-in ~python-mode~. Here, the focus is on one Emacs package and three Python
|
||
packages:
|
||
1. [[#sec:eglot][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.
|
||
2. [[https://jedi.readthedocs.io/en/latest/][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.
|
||
3. Nowadays, [[https://github.com/charliermarsh/ruff#readme][Ruff]] is the fastest Python linter and a replacement for [[https://flake8.pycqa.org/en/latest/][Flake8]] with
|
||
a variety of its plugins. [[https://notes.crmarsh.com/][Charlie Marsh]] explains why he started [[https://pypi.org/project/ruff/][Ruff]] in the
|
||
post [[https://notes.crmarsh.com/python-tooling-could-be-much-much-faster][Python tooling could be much faster]].
|
||
4. [[https://github.com/python-lsp/python-lsp-server#readme][Python LSP Server]] is in my opinion the most complete [[https://en.wikipedia.org/wiki/Language_Server_Protocol][Language Server Protocol]]
|
||
implementation for Python and it handles [[https://github.com/charliermarsh/ruff#readme][Ruff]] thanks to the [[https://github.com/python-lsp/python-lsp-ruff][python-lsp-ruff]]
|
||
plugin.
|
||
|
||
Here are links covering how to integrate Emacs, Python and a Python LSP
|
||
server, before plunging into the configuration steps:
|
||
1. [[https://taingram.org/blog/emacs-lsp-ide.html][Building Your Own Emacs IDE with LSP]]
|
||
2. [[https://rgoswami.me/posts/emacs-lang-servers/][Doom Emacs and Language Servers]]
|
||
3. [[https://ddavis.io/posts/eglot-python-ide/][Eglot based Emacs Python IDE]]
|
||
4. [[https://www.mattduck.com/lsp-python-getting-started.html][Getting started with lsp-mode for Python]]
|
||
5. [[https://ddavis.io/posts/python-emacs-3/][Python & Emacs, Take 3]]
|
||
6. [[https://ddavis.io/posts/emacs-python-lsp/][Python with Emacs: py(v)env and lsp-mode]]
|
||
|
||
Here are links to Python programming background videos:
|
||
1. [[yt:T-TwcmT6Rcw][Dataclasses: The Code Generator to End All Code Generators - Raymond Hettinger]]
|
||
2. [[yt:p33CVV29OG8][Modern Dictionaries - Raymond Hettinger]]
|
||
3. [[yt:HTLu2DFOdTg][Python's Class Development Kit - Raymond Hettinger]]
|
||
4. [[yt:uL_kmagVKFQ][The Big Leap of Python-3.13 - Lukasz Langa]]
|
||
5. [[yt:UANN2Eu6ZnM][The Mental Game of Python - Raymond Hettinger]]
|
||
6. [[yt:Bv25Dwe84g0][Thinking about Concurrency - Raymond Hettinger]]
|
||
7. [[yt:0kXaLh8Fz3k][Thinking outside the GIL with AsyncIO and MultiProcessing - John Reese]]
|
||
8. [[yt:bZAsSzzk0E][What can't WebAssembly do? - Katie Bell]]
|
||
|
||
*** [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][Python-mode]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:python-mode
|
||
:END:
|
||
|
||
Listing [[lst:setup-python-mode][setup Python mode]] selects a common Python interpreter in a virtual
|
||
environment for use in =python-mode= and =ob-python=. The [[https://github.com/pythonic-emacs/pythonic#readme][pythonic]], [[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]], and
|
||
[[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]] packages allow to handle Python virtual environments within Emacs. The
|
||
[[https://github.com/pyenv/pyenv][pyenv]] package provides support to work with [[https://github.com/pyenv/pyenv#readme][pyenv]] (eventually with
|
||
[[https://github.com/pyenv/pyenv-virtualenv#readme][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 [[https://github.com/pyenv/pyenv#readme][pyenv]] and [[https://github.com/pythonic-emacs/pythonic#readme][pythonic]]) or tweak the
|
||
environment variables and restart the relevant Python child processes (in case
|
||
of [[https://github.com/jorgenschaefer/pyvenv#readme][pyvenv]]). Therefore, I replace those packages with listing [[lst:access-pyenv][access pyenv]] and
|
||
[[lst:select-python-virtual-environment][select a Python interpreter in a virtual environment]] to set
|
||
=python-shell-virtualenv-root= from within Emacs.
|
||
|
||
Install [[https://github.com/PyCQA/isort#readme][isort]] and [[https://github.com/PyCQA/pyflakes#readme][pyflakes]] to enable adding, removing, sorting, and fixing
|
||
import statements in [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][Python-mode]], see the end of the commentary
|
||
src_emacs-lisp[:results none]{(find-library "python")} section.
|
||
|
||
Listing [[lst:pyproject-toml-kickoff][kickoff pyproject.toml proposal]] facilitates dropping a [[https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml][pyproject.toml]]
|
||
file into Python projects which anyhow should switch to using a [[https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml][pyproject.toml]]
|
||
file as explained in the post [[https://bbc.github.io/cloudfit-public-docs/packaging/this_way_up.html][This Way Up: A Bottom-Up Look At Python Packaging]].
|
||
The [[https://packaging.python.org/en/latest/tutorials/packaging-projects/][packaging Python projects tutorial]] and the [[https://github.com/pypa/sampleproject][Python sample project]] walk you
|
||
through the process of writing a [[https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml][pyproject.toml]] file. [[https://github.com/charliermarsh/ruff#does-ruff-support-numpy--or-google-style-docstrings][Ruff docstring setup]]
|
||
explains how to setup documentation string checking in the [[https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml][pyproject.toml]] file.
|
||
|
||
Finally, listing [[lst:ruff-nocolor][ruff-nocolor]] pipes the =stdout= output of the [[https://pypi.org/project/ruff/][ruff]] executable
|
||
through =cat= to remove escape sequences.
|
||
|
||
#+caption[Setup Python mode with =ob-python=]:
|
||
#+caption: Setup Python mode with =ob-python=.
|
||
#+name: lst:setup-python-mode
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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" "-")))
|
||
#+end_src
|
||
|
||
#+caption[Access =pyenv=]:
|
||
#+caption: Access =pyenv=.
|
||
#+name: lst:access-pyenv
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (executable-find "pyenv")
|
||
(defun pyenv-full-path (version)
|
||
"Return the full path for VERSION."
|
||
(concat (pyenv-root) (file-name-as-directory "versions") version))
|
||
|
||
(defun pyenv-root ()
|
||
"Return \"pyenv root\" as a directory."
|
||
(let* ((pair (get-program-code-output "pyenv" "root"))
|
||
(return-code (car pair))
|
||
(output (cdr pair)))
|
||
(if (= 0 return-code)
|
||
(file-name-as-directory (string-trim output))
|
||
(error "%s" (string-trim output)))))
|
||
|
||
(defun pyenv-version-name ()
|
||
"Return \"pyenv version-name\"."
|
||
(let* ((pair (get-program-code-output "pyenv" "version-name"))
|
||
(return-code (car pair))
|
||
(output (cdr pair)))
|
||
(if (= 0 return-code)
|
||
(string-trim output)
|
||
(error "%s" (string-trim output)))))
|
||
|
||
(defun pyenv-versions ()
|
||
"Return \"pyenv versions --bare --skip-aliases\" as a list."
|
||
(let* ((pair (get-program-code-output
|
||
"pyenv" "versions" "--bare" "--skip-aliases"))
|
||
(return-code (car pair))
|
||
(output (cdr pair)))
|
||
(if (= 0 return-code)
|
||
(split-string output)
|
||
(error "%s" (string-trim output)))))
|
||
|
||
(defun pyenv-virtualenvs ()
|
||
"Return \"pyenv virtualenvs --bare --skip-aliases\" as a list."
|
||
(let* ((pair (get-program-code-output
|
||
"pyenv" "virtualenvs" "--bare" "--skip-aliases"))
|
||
(return-code (car pair))
|
||
(output (cdr pair)))
|
||
(if (= 0 return-code)
|
||
(split-string output)
|
||
(error "%s" (string-trim output))))))
|
||
#+end_src
|
||
|
||
#+caption[Select the Python virtual environment]:
|
||
#+caption: Select the Python virtual environment.
|
||
#+name: lst:select-python-virtual-environment
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'python
|
||
(when (and (fboundp 'pyenv-full-path)
|
||
(fboundp 'pyenv-version-name)
|
||
(fboundp 'pyenv-versions)
|
||
(fboundp '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-virtualenv-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-virtualenv-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)))))
|
||
#+end_src
|
||
|
||
#+caption[Kickoff =pyproject.toml= proposal]:
|
||
#+caption: Kickoff =pyproject.toml= proposal.
|
||
#+name: lst:pyproject-toml-kickoff
|
||
#+begin_src toml -n :tangle pyproject.toml
|
||
# 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
|
||
|
||
[tool.ruff.lint]
|
||
select = [
|
||
"ARG", # flake8-unused-arguments
|
||
"B", # flake8-bugbear
|
||
"C", # mccabe
|
||
"C4", # flake8-comprehensions
|
||
"E", # pycodestyle
|
||
"D", # pydocstyle
|
||
"F", # pyflakes
|
||
"UP", # pyupgrade
|
||
"W", # pycodestyle
|
||
"NPY201", # numpy-2.0 upgrade guide
|
||
]
|
||
ignore = [
|
||
"B905", # `zip()` without an explicit `strict=` parameter
|
||
"D202", # no blank lines allowed after function docstring
|
||
]
|
||
|
||
[tool.ruff.lint.mccabe]
|
||
max-complexity = 15
|
||
|
||
[tool.ruff.lint.pydocstyle]
|
||
convention = "numpy"
|
||
|
||
# Local Variables:
|
||
# mode: conf-toml-mode
|
||
# End:
|
||
#+end_src
|
||
|
||
#+caption[Wrap =ruff= to remove color from text output]:
|
||
#+caption: Wrap =ruff= to remove color from text output.
|
||
#+header: :tangle-mode (identity #o755)
|
||
#+name: lst:ruff-nocolor
|
||
#+begin_src shell -n :eval never :tangle ~/bin/ruff-nocolor
|
||
#!/bin/sh
|
||
|
||
ruff "$@" | cat
|
||
|
||
# Local Variables:
|
||
# mode: shell-script
|
||
# sh-indentation: 2
|
||
# sh-basic-offset: 2
|
||
# End:
|
||
#+end_src
|
||
|
||
*** [[https://pip.pypa.io/en/stable/][Package Installer for Python]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:pip-python
|
||
:header-args: :eval never-export
|
||
:END:
|
||
|
||
Listing [[lst:pip-pypi-options]], [[lst:pip-list-outdated]], and [[lst:pip-upgrade-maybe]]
|
||
define the =pip-list-outdated= and =pip-upgrade-maybe= commands to upgrade
|
||
outdated Python packages in Emacs.
|
||
|
||
#+caption[Emacs client "Package Installer for Python" and "PyPI" options]:
|
||
#+caption: Emacs client "Package Installer for Python" and "PyPI" options.
|
||
#+name: lst:pip-pypi-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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"))
|
||
#+end_src
|
||
|
||
#+caption[Emacs interface to list outdated Python packages]:
|
||
#+caption: Emacs interface to list outdated Python packages.
|
||
#+name: lst:pip-list-outdated
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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 ((content '("pip" "list" "--outdated" "--format" "json")))
|
||
(make-process
|
||
:name "pip-list-outdated"
|
||
:buffer (generate-new-buffer-name "*pip-list-outdated-output*")
|
||
:command content
|
||
:sentinel #'pip--list-outdated-sentinel)
|
||
(message "Running `%s' asynchronously" (string-join content " "))))
|
||
#+end_src
|
||
|
||
#+caption[Emacs interface to upgrade outdated unfrozen Python packages]:
|
||
#+caption: Emacs interface to upgrade outdated unfrozen Python packages.
|
||
#+name: lst:pip-upgrade-maybe
|
||
#+begin_src emacs-lisp :results silent
|
||
(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 ((content
|
||
`("pip" "install" "--progress-bar" "off" ,@(nreverse found))))
|
||
(make-process
|
||
:name "pip-upgrade-maybe"
|
||
:buffer (generate-new-buffer-name "*pip-upgrade-maybe*")
|
||
:command content
|
||
:sentinel #'pip--upgrade-maybe-sentinel)
|
||
(message "Running `%s' asynchronously" (string-join content " ")))
|
||
(message "`pip-upgrade-maybe' found no packages to install"))))
|
||
#+end_src
|
||
|
||
*** [[https://github.com/joaotavora/eglot][Eglot]] for [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][python-mode]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:eglot-python
|
||
:END:
|
||
|
||
Listing [[lst:configure-eglot+pylsp-ruff]] configures [[https://github.com/joaotavora/eglot][eglot]] for [[https://www.python.org][Python]] using the
|
||
[[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] with the [[https://github.com/python-lsp/python-lsp-ruff#readme][pylsp-ruff]] plugin for the easiest way to let
|
||
[[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] use [[https://pypi.org/project/ruff/][Ruff]]. It is better to uninstall the Python packages
|
||
[[https://github.com/hhatto/autopep8#readme][autopep8]], [[https://github.com/PyCQA/flake8][flake8]], [[https://github.com/PyCQA/pydocstyle#readme][pydocstyle]], [[https://github.com/PyCQA/pylint#readme][pylint]], and [[https://github.com/python-rope/rope#readme][rope]].
|
||
|
||
Listing [[lst:eglot-directory-variables-for-python]] shows a proper [[info:emacs#Directory Variables][.dir-locals.el]]
|
||
file in the root directory of any [[https://www.python.org][Python]] project to start [[https://github.com/joaotavora/eglot][eglot]] automatically.
|
||
|
||
Type {{{kbd(M-x eglot-show-workspace-configuration)}}} to dump a =JSON=
|
||
representation of src_emacs-lisp[:results none]{(describe-variable
|
||
'eglot-workspace-configuration)} for debugging.
|
||
|
||
#+caption[Configure =eglot= with =python-lsp-ruff=]:
|
||
#+caption: Configure =eglot= with =python-lsp-ruff=.
|
||
#+name: lst:configure-eglot+pylsp-ruff
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'eglot
|
||
(setq-default
|
||
eglot-workspace-configuration
|
||
;; Enable the `:pylsp_ruff' plugin and ensure to uninstall the
|
||
;; `:flake8', `:mccabe', and `:pycodestye' plugins.
|
||
'(:pylsp (:plugins
|
||
(:pylsp_ruff
|
||
(:enabled t)
|
||
:jedi
|
||
(:auto_import_modules ["numpy"])
|
||
:jedi_completion
|
||
(:cache_for ["astropy"]))))))
|
||
#+end_src
|
||
|
||
#+caption[A =.dir-locals.el= proposal to launch =eglot= automatically]:
|
||
#+caption: A =.dir-locals.el= file proposal for Python projects or Org-mode
|
||
#+caption: projects tangling Python files to launch =eglot= automatically.
|
||
#+name: lst:eglot-directory-variables-for-python
|
||
#+begin_src emacs-lisp -n :eval never :tangle dir-locals.el
|
||
;; A .dir-locals.el file proposal in the root of any
|
||
;; Python project or Org-mode project tangling Python files
|
||
;; to launch eglot automatically.
|
||
;; Ensure to enable the pylsp_ruff plugin.
|
||
;; Ensure to uninstall the flake8, mccabe, and pycodestyle Python packages.
|
||
((nil ;; nil, since Emacs filters out irrelevant mode names.
|
||
. ((eglot-workspace-configuration
|
||
. (:pylsp (:plugins
|
||
(:pylsp_ruff
|
||
(:enabled t)
|
||
:jedi
|
||
(:auto_import_modules ["numpy"])
|
||
:jedi_completion
|
||
(:cache_for ["astropy"]))))))))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Eglot related key bindings]:
|
||
#+caption: Eglot related key-bindings.
|
||
#+name: tab:eglot-related-key-bindings
|
||
|---------------------------+----------------+------------------|
|
||
| command | key map | keys |
|
||
|---------------------------+----------------+------------------|
|
||
| *xref-find-definition* | global-map | {{{kbd(M-.)}}} |
|
||
| *xref-pop* | global-map | {{{kbd(M-\,)}}} |
|
||
| *flymake-goto-next-error* | eglot-mode-map | {{{kbd(C-c n)}}} |
|
||
| *flymake-goto-prev-error* | eglot-mode-map | {{{kbd(C-c p)}}} |
|
||
| *eglot-rename* | eglot-mode-map | {{{kbd(C-c r)}}} |
|
||
| *eldoc-doc-buffer* | eglot-mode-map | {{{kbd(C-h .)}}} |
|
||
|---------------------------+----------------+------------------|
|
||
|
||
*** [[https://jedi.readthedocs.io/en/latest/][Jedi]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:jedi
|
||
:END:
|
||
|
||
Listing [[lst:example-py]] is a [[https://www.python.org][Python]] example to test whether [[https://jedi.readthedocs.io/en/latest/][jedi]] in combination
|
||
with [[https://github.com/joaotavora/eglot][eglot]] works when coding for instance [[https://numpy.org/][numpy]] universal functions.
|
||
|
||
#+caption[Tangle the =example.py= file]:
|
||
#+caption: Tangle the =example.py= file.
|
||
#+name: lst:example-py
|
||
#+begin_src python -n :tangle example.py
|
||
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)
|
||
#+end_src
|
||
|
||
*** [[https://stackoverflow.com/questions/2324758/debugging-python-programs-in-emacs][Debugging Python programs in Emacs]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:debug-python
|
||
:END:
|
||
|
||
[[https://github.com/realgud/realgud][RealGUD]] is the best option and in case of defaulting to {{{kbd(M-x pdb)}}}
|
||
debugging may work better with =tool-bar-mode= enabled. Listing
|
||
[[lst:ensure-realgud]] ensures the installation of =realgud=. Listing [[lst:pdb-numpy]]
|
||
is a debugging start by means of =pdb= or =realgud:pdb=. Links of interest are:
|
||
- [[https://github.com/realgud/realgud][RealGUD]] provides a nice interface to PDB by means of {{{kbd(M-x
|
||
realgud:pdb)}}} that also works well with =tool-bar-mode= disabled.
|
||
- [[https://realpython.com/python-debugging-pdb/][Python Debugging with PDB]]
|
||
- [[https://docs.python.org/3/library/pdb.html][The Python Debugger]]
|
||
- [[yt:vfPtGsSJldg][Introduction to PDB (Python-2.7 YouTube video)]]
|
||
- [[https://stackoverflow.com/questions/7668979/how-do-you-watch-a-variable-in-pdb][How do you watch a variable in PDB? (Python-2.7)]]
|
||
- [[https://stackoverflow.com/questions/51349074/in-python-3-7-does-the-opcode-event-from-sys-settrace-give-any-information-ab][How to use the opcode event (Python-3.7, GAV: why does it fail)?]]
|
||
- [[https://pypi.org/project/pdbpp/][PDB++]] looks incompatible with Python>3.10: see [[https://github.com/pdbpp/pdbpp/issues/516][Python-3.11 breaks PDBPP]].
|
||
- [[https://python-scientific-lecture-notes.developpez.com/tutoriel/notes-cours/python-debugging-code/#L3-1][Debugging Python with IPDB under IPython or with PDB in a terminal]].
|
||
- [[https://docs.spyder-ide.org/current/panes/debugging.html][Spyder Python debugging using IPDB]].
|
||
- [[https://docs.python.org/3/howto/gdb_helpers.html][Debugging C API extensions and CPython Internals with GDB]]. Making this work
|
||
with =pyenv= is an unresolved issue: see [[https://github.com/pyenv/pyenv/issues/1190][Install python-gdb.py]].
|
||
Python info links of interest are:
|
||
- [[info:python#Debugging C API extensions and CPython Internals with GDB][Debugging C API extensions and CPython Internals with GDB (info)]].
|
||
- [[info:python#Debugging and Profiling][Debugging and Profiling (info)]].
|
||
|
||
#+caption[Ensure =realgud= installation]:
|
||
#+caption: Ensure =realgud= installation.
|
||
#+name: lst:ensure-realgud
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(ensure-package-installation 'realgud)
|
||
#+end_src
|
||
|
||
#+caption[PDB NumPy example: =python -m pdb .emacs.d/nuggy.py=]:
|
||
#+caption: PDB NumPy example: =python -m pdb .emacs.d/nuggy.py=.
|
||
#+name: lst:pdb-numpy
|
||
#+begin_src python -i -n :results silent
|
||
def buggy():
|
||
from numpy import array, matrix
|
||
from numpy.linalg import inv
|
||
|
||
a = matrix(array([[1.0, 1.0], [1.0, 1.0]]))
|
||
# Use "display", "p", or "pp" to inspect "a".
|
||
# PDB: A series of "s" does not raise the exception here, contrary to "c".
|
||
b = inv(a)
|
||
return b
|
||
|
||
if __name__ == "__main__":
|
||
b = buggy()
|
||
# PDB: The series of "s" says only here "Uncaught exception".
|
||
#+end_src
|
||
|
||
* [[https://github.com/emacs-tw/awesome-emacs#library][Libraries]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:libraries
|
||
:END:
|
||
|
||
** [[info:dash.info#Top][Library dash.el (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:dash-library
|
||
:END:
|
||
|
||
The library [[info:dash.info#Top][dash.el (info)]] positions itself as a modern alternative for the list
|
||
processing API of [[info:cl#Top][cl]]. It is a requirement of important packages in this Emacs
|
||
setup (for instance [[https://github.com/andras-simonyi/citeproc-el#readme][citeproc]], [[https://github.com/magit/magit#readme][magit]], and [[https://github.com/Fuco1/smartparens#readme][smartparens]]). Listing
|
||
[[lst:configure-dash]] enables fontification of =dash= macros and makes
|
||
=info-lookup-symbol= take into account the =dash= macros.
|
||
|
||
#+caption[Configure =dash= fontification and info lookup]:
|
||
#+caption: Configure =dash= fontification and info lookup.
|
||
#+name: lst:configure-dash
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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)))
|
||
#+end_src
|
||
|
||
** [[https://github.com/rejeep/f.el][Library f.el]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:f-library
|
||
:END:
|
||
|
||
The library [[https://github.com/rejeep/f.el][f.el]] positions itself as a modern API for working with files and
|
||
directories in Emacs. It is a requirement of the [[https://github.com/andras-simonyi/citeproc-el#readme][citeproc]] package in this Emacs
|
||
setup and requires no configuration.
|
||
|
||
** [[https://github.com/magnars/s.el][Library s.el]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:s-library
|
||
:END:
|
||
|
||
The library [[https://github.com/magnars/s.el][s.el]] positions itself as the long lost Emacs string manipulation
|
||
library. It is a requirement of the [[https://github.com/andras-simonyi/citeproc-el#readme][citeproc]] and [[https://github.com/bdarcus/citar#readme][citar]] packages in this Emacs
|
||
setup and requires no configuration.
|
||
|
||
* [[info:emacs#Minor Modes][Minor Modes (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:minor-modes
|
||
:END:
|
||
|
||
** [[https://github.com/victorhge/iedit#readme][Synchronal multiple-region editing]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:synchronal-multiple-region-editing
|
||
:END:
|
||
|
||
#+caption[Enable =iedit=]:
|
||
#+caption: Enable =iedit=.
|
||
#+name: lst:enable-iedit
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'iedit)
|
||
(require 'iedit nil 'noerror))
|
||
#+end_src
|
||
|
||
** [[https://github.com/lewang/ws-butler#readme][Unobtrusive whitespace trimming]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:unobtrusive-whitespace-trimming
|
||
:END:
|
||
|
||
#+caption[Configure =ws-butler=]:
|
||
#+caption: Configure =ws-butler=.
|
||
#+name: lst:configure-ws-butler
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'ws-butler)
|
||
(setopt ws-butler-keep-whitespace-before-point nil)
|
||
(add-hook 'prog-mode-hook #'ws-butler-mode)
|
||
(add-hook 'text-mode-hook #'ws-butler-mode))
|
||
#+end_src
|
||
|
||
** [[https://countvajhula.com/2021/09/25/the-animated-guide-to-symex/][Structural editing]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:structural-editing
|
||
:END:
|
||
|
||
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 [[https://github.com/Fuco1/smartparens][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 [[https://smartparens.readthedocs.io/en/latest/index.html][smartparens documentation]] targets experienced Emacs users. The
|
||
following links show how to put the documentation to practical use:
|
||
1. The [[https://gist.github.com/pvik/8eb5755cc34da0226e3fc23a320a3c95][smartparens cheatsheet]] demonstrates its usage visually.
|
||
2. [[https://gist.github.com/oantolin][Omar Antolin's gist "my-smartparens-config.el"]] is the first place to look for
|
||
how to tweak [[https://github.com/Fuco1/smartparens][smartparens]]. However, the gist may be partially obsolete, since
|
||
it is not part of his current [[https://github.com/oantolin/emacs-config][Emacs configuration]].
|
||
3. [[https://lists.gnu.org/archive/html/help-gnu-emacs/2014-07/msg00135.html][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:setup-smartparens]] aims to setup [[https://github.com/Fuco1/smartparens][smartparens]] for Go, LaTeX, Lisp
|
||
dialects, Org, and Python. Execute src_emacs-lisp[:results
|
||
none]{(sp-cheat-sheet)} for short documentation taking into account the
|
||
overridden key bindings in listing [[lst:setup-smartparens]]. Table
|
||
[[tab:smartparens-commands-and-bindings]] lists commands with key bindings taken in
|
||
order from src_emacs-lisp[:results none]{(sp-cheat-sheet)} that takes the
|
||
overrides of listing [[lst:setup-smartparens]] into account. Finally, listing
|
||
[[lst:sp-eval-expression]] defines an alternative to =eval-expression= enabling
|
||
=smartparens-strict-mode= and =font-lock-mode=.
|
||
|
||
Despite the provocative post [[https://andreyorst.gitlab.io/posts/2022-02-20-what-if-structural-editing-was-a-mistake/]["What if structural editing was a mistake?"]],
|
||
[[https://github.com/Fuco1/smartparens][smartparens]] is one of my favorite packages. In particular, [[https://github.com/Fuco1/smartparens][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, [[https://smartparens.readthedocs.io/en/latest/index.html?highlight=show-smartparens-mode#getting-started][show-smartparens-mode]] highlights
|
||
matching pairs immediately in front or after point in such files correctly,
|
||
contrary to for instance [[https://github.com/Fanael/rainbow-delimiters#readme][rainbow-delimiters]].
|
||
|
||
#+caption[Setup =smartparens=]:
|
||
#+caption: Setup =smartparens=.
|
||
#+name: lst:setup-smartparens
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'smartparens)
|
||
;; GAV: Documentation says to require `smartparens-config'.
|
||
(require 'smartparens-config)
|
||
(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)))
|
||
|
||
(add-hook 'conf-toml-mode-hook #'smartparens-mode)
|
||
(add-hook 'prog-mode-hook #'smartparens-mode)
|
||
(add-hook 'text-mode-hook #'smartparens-mode)
|
||
|
||
(add-hook 'emacs-lisp-mode-hook #'smartparens-strict-mode)
|
||
(add-hook 'go-ts-mode-hook #'smartparens-strict-mode)
|
||
(add-hook 'ielm-mode-hook #'smartparens-strict-mode)
|
||
(add-hook 'inferior-python-mode-hook #'smartparens-strict-mode)
|
||
(add-hook 'lisp-data-mode-hook #'smartparens-strict-mode)
|
||
(add-hook 'lisp-mode-hook #'smartparens-strict-mode)
|
||
(add-hook 'python-mode-hook #'smartparens-strict-mode)
|
||
(add-hook 'sly-mrepl-mode-hook #'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))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Smartparens commands with key bindings in this setup]:
|
||
#+caption: Smartparens commands with key bindings in this setup.
|
||
#+name: tab:smartparens-commands-and-bindings
|
||
|----------------------------------+----------------------------+----------|
|
||
| command | keys | status |
|
||
|----------------------------------+----------------------------+----------|
|
||
| *sp-forward-sexp* | {{{kbd(C-M-f)}}} | |
|
||
| *sp-backward-sexp* | {{{kbd(C-M-b)}}} | |
|
||
| *sp-next-sexp* | {{{kbd(C-M-n)}}} | |
|
||
| *sp-prev-sexp* | {{{kbd(C-M-p)}}} | |
|
||
| *sp-down-sexp* | {{{kbd(C-M-d)}}} | shadowed |
|
||
| *sp-backward-down-sexp* | {{{kbd(C-M-a)}}} | |
|
||
| *sp-beginning-of-sexp* | {{{kbd(C-S-d)}}} | |
|
||
| *sp-end-of-sexp* | {{{kbd(C-S-a)}}} | |
|
||
| *sp-up-sexp* | {{{kbd(C-M-e)}}} | |
|
||
| *sp-backward-up-sexp* | {{{kbd(C-M-u)}}} | |
|
||
| *sp-kill-sexp* | {{{kbd(C-M-k)}}} | |
|
||
| *sp-copy-sexp* | {{{kbd(C-M-w)}}} | |
|
||
| *sp-forward-slurp-sexp* | {{{kbd(C-))}}} | override |
|
||
| *sp-backward-slurp-sexp* | {{{kbd(C-()}}} | override |
|
||
| *sp-forward-barf-sexp* | {{{kbd(C-M-))}}} | override |
|
||
| *sp-backward-barf-sexp* | {{{kbd(C-M-()}}} | override |
|
||
| *sp-forward-symbol* | {{{kbd(M-F)}}} | |
|
||
| *sp-backward-symbol* | {{{kbd(M-B)}}} | |
|
||
| *sp-unwrap-sexp* | {{{kbd(M-<delete>)}}} | shadowed |
|
||
| *sp-backward-unwrap-sexp* | {{{kbd(M-<backspace>)}}} | |
|
||
| *sp-splice-sexp* | {{{kbd(M-D)}}} | |
|
||
| *sp-splice-killing-backward* | {{{kbd(C-M-<backspace>)}}} | |
|
||
| *sp-splice-sexp-killing-forward* | {{{kbd(C-M-<delete>)}}} | shadowed |
|
||
| *sp-select-next-thing* | {{{kbd(C-M-])}}} | |
|
||
| *sp-select-next-thing-exchange* | {{{kbd(C-])}}} | |
|
||
| *sp-mark-sexp* | {{{kbd(C-M-SPC)}}} | |
|
||
|----------------------------------+----------------------------+----------|
|
||
|
||
#+caption[Define =sp-eval-expression= with =smartparens= support]:
|
||
#+caption: Define =sp-eval-expression= enabling =smartparens-strict-mode=
|
||
#+caption: and =font-lock-mode=.
|
||
#+name: lst:sp-eval-expression
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'smartparens
|
||
;; https://lists.gnu.org/archive/html/help-gnu-emacs/2014-07/msg00135.html
|
||
;; GAV: Reuse `read--expresssion-map' instead of defining my own map.
|
||
(defun sp--read-expression (prompt &optional initial-contents)
|
||
(let ((minibuffer-completing-symbol t))
|
||
(minibuffer-with-setup-hook
|
||
(lambda ()
|
||
(emacs-lisp-mode) ; Enables `smartparens-strict-mode' too.
|
||
(use-local-map read--expression-map)
|
||
(font-lock-mode t))
|
||
(read-from-minibuffer prompt initial-contents
|
||
read--expression-map nil
|
||
'read-expression-history))))
|
||
|
||
(defun sp-eval-expression (expression &optional arg)
|
||
"Evaluate EXPRESSION with `smartparens' support."
|
||
(interactive (list (read (sp--read-expression "SP eval: "))
|
||
current-prefix-arg))
|
||
(if arg
|
||
(insert (pp-to-string (eval expression lexical-binding)))
|
||
(pp-display-expression (eval expression lexical-binding)
|
||
"*SP Eval Output*")))
|
||
|
||
;; Do not change the "M-ESC :" `eval-expression' key binding.
|
||
(keymap-global-set "M-:" #'sp-eval-expression))
|
||
#+end_src
|
||
|
||
** [[https://github.com/davidshepherd7/electric-operator#readme][Electric operators]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:electric-operators
|
||
:END:
|
||
|
||
Listing [[lst:configure-electric-operator]] configures =electric-operator-mode= to
|
||
add spaces around operators for compatibility with for instance the [[https://black.readthedocs.io/en/stable/][Black code
|
||
formatter for Python]].
|
||
|
||
#+caption[Configure =electric-operator=]:
|
||
#+caption: Configure =electric-operator=.
|
||
#+name: lst:configure-electric-operator
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'electric-operator)
|
||
(add-hook 'c-mode-common-hook #'electric-operator-mode)
|
||
(add-hook 'python-mode-hook #'electric-operator-mode))
|
||
#+end_src
|
||
|
||
** [[https://joaotavora.github.io/yasnippet/][Smart snippets]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:smart-snippets
|
||
:END:
|
||
|
||
#+caption[Setup =yasnippet=]:
|
||
#+caption: Setup =yasnippet=.
|
||
#+name: lst:setup-yasnippet
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'yasnippet)
|
||
;; Set `yas-alias-to-yas/prefix-p' before loading `yasnippet'.
|
||
(setopt yas-alias-to-yas/prefix-p nil)
|
||
(add-hook 'LaTeX-mode-hook #'yas-minor-mode)
|
||
(add-hook 'org-mode-hook #'yas-minor-mode)
|
||
(add-hook 'python-mode-hook #'yas-minor-mode))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Display][Display (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:display
|
||
:END:
|
||
|
||
** [[info:emacs#Narrowing][Narrowing]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:narrowing
|
||
:END:
|
||
|
||
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" [[https://endlessparentheses.com/emacs-narrow-or-widen-dwim.html][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.
|
||
|
||
#+caption[Configure =narrow-or-widen-dwim=]:
|
||
#+caption: Configure =narrow-or-widen-dwim=.
|
||
#+name: lst:configure-narrow-or-widen-dwim
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(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))
|
||
#+end_src
|
||
|
||
** [[https://jblevins.org/log/rainbow-mode][Visualize color codes and names]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:rainbow-mode
|
||
:END:
|
||
|
||
Listing [[lst:setup-rainbow-mode]] configures =rainbow-mode= to colorize color codes
|
||
and names in buffers for debugging.
|
||
|
||
#+caption[Setup =rainbow-mode=]:
|
||
#+caption: Setup =rainbow-mode=.
|
||
#+name: lst:setup-rainbow-mode
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'rainbow-mode)
|
||
;; GAV: Do not add `rainbow-mode' to any programming language hook,
|
||
;; since that interferes at least with Org export to LaTeX.
|
||
(setopt rainbow-x-colors-major-mode-list
|
||
(list 'c++-mode 'c-mode 'emacs-lisp-mode 'inferior-emacs-lisp-mode
|
||
'lisp-interaction-mode 'org-mode 'python-mode)))
|
||
#+end_src
|
||
|
||
** [[https://karthinks.com/software/batteries-included-with-emacs/][Flash the line around point for visual feedback]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:flash-line-around-point
|
||
:END:
|
||
|
||
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.
|
||
|
||
#+caption[Implement =flash-line-around-point=]:
|
||
#+caption: Implement =flash-line-around-point=.
|
||
#+name: lst:flash-line-around-point
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; https://karthinks.com/software/batteries-included-with-emacs/
|
||
;; https://github.com/karthink/.emacs.d/blob/master/init.el#L2077
|
||
;; BUG#71537: Using `let' in `flash-line-around-point' implies
|
||
;; requiring `pulse'.
|
||
(require 'pulse)
|
||
|
||
(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))))
|
||
|
||
(advice-add 'scroll-up-command :after #'flash-line-around-point)
|
||
(advice-add 'scroll-down-command :after #'flash-line-around-point)
|
||
(advice-add 'recenter-top-bottom :after #'flash-line-around-point)
|
||
(advice-add 'other-window :after #'flash-line-around-point)
|
||
#+end_src
|
||
|
||
* Applications
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:applications
|
||
:END:
|
||
|
||
** [[info:emacs#Hyperlinking][Hyperlinking and Web Navigation Features (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:hyperlinking-web-navigating
|
||
:END:
|
||
|
||
*** [[info:emacs#Browse-URL][Browse URL (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:browse-url
|
||
:END:
|
||
|
||
Listing [[lst:configure-browse-url]] configures =browse-url=.
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Configure =browse-url=]:
|
||
#+caption: Configure =browse-url=.
|
||
#+name: lst:configure-browse-url
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'browse-url
|
||
(defun browse-url-mpv (url &optional _)
|
||
(start-process "mpv" nil "mpv" url))
|
||
|
||
(defconst browse-url-mpv-regexp
|
||
(rx bos "http" (opt "s") "://"
|
||
(or (seq "youtu.be/")
|
||
(seq "www.youtube.com/" (or "embed/" "watch?"))
|
||
(seq (+? nonl) (or ".mp4" ".webm") eos)))
|
||
"Match hyperlinks to open with mpv.")
|
||
|
||
(setopt
|
||
browse-url-generic-program (or (when (eq system-type 'darwin) "open")
|
||
(executable-find "firefox"))
|
||
browse-url-handlers `((,browse-url-mpv-regexp . browse-url-mpv)
|
||
("." . eww-browse-url))))
|
||
#+end_src
|
||
|
||
*** [[https://en.wikipedia.org/wiki/Eww_(web_browser)][Emacs Web Wowser]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-web-wowser
|
||
:END:
|
||
|
||
#+caption[Display =pdf= links properly in =eww=]:
|
||
#+caption: Display =pdf= links properly in =eww=.
|
||
#+name: lst:display-pdf-links-in-eww
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'eww
|
||
;; GAV: Renaming `eww' buffers conflicts with `desktop-save-mode'.
|
||
;; GAV: Setting `eww-restore-desktop' to something else fails too.
|
||
(setopt eww-restore-desktop nil)
|
||
|
||
(defun eww-display-pdf-as-binary (fn &rest args)
|
||
(let ((buffer-file-coding-system 'binary))
|
||
(apply fn args)))
|
||
|
||
(advice-add 'eww-display-pdf :around #'eww-display-pdf-as-binary))
|
||
#+end_src
|
||
|
||
*** [[https://en.wikipedia.org/wiki/Media_type#Mailcap][Mailcap]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:mailcap
|
||
:END:
|
||
|
||
Extension packages as [[info:emacs#EWW][EWW (info)]], [[info:emacs#Gnus][Gnus (info)]], and [[info:org#Top][Org (info)]] rely on a [[https://en.wikipedia.org/wiki/Media_type#Mailcap][mailcap]]
|
||
file to know what application should open a specific media file type. Listing
|
||
[[lst:tangle-mailcap-file]] specifies =emacsclient= as the application to open PDF
|
||
files.
|
||
|
||
#+caption[Tangle =mailcap= file]:
|
||
#+caption: Tangle =mailcap= file.
|
||
#+name: lst:tangle-mailcap-file
|
||
#+begin_src org -n :tangle ~/.mailcap
|
||
# https://emacs.stackexchange.com/a/24502 answers the question:
|
||
# How to use pdf-tools (pdf-view-mode) in emacs?
|
||
application/pdf; emacsclient %s
|
||
#+end_src
|
||
|
||
*** [[https://github.com/minad/osm#readme][Open Street Map viewer for Emacs]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:open-street-map
|
||
:END:
|
||
|
||
[[https://github.com/minad/osm#readme][Open Street Map]] is a tile-based map viewer with a responsive movable and
|
||
zoomable display and with a list of multiple preconfigured tile servers.
|
||
Listing [[lst:ensure-osm-installation]] ensures the installation of ~osm~ and
|
||
listing [[lst:using-osm-example]] is a minimal example of how to use ~osm~. Inside
|
||
~osm-mode~ buffers, the key binding for the command
|
||
src_emacs-lisp{(osm-bookmark-set)} is {{{kbd(C-x r b)}}} allowing to bookmark
|
||
such buffers. Outside ~osm-mode~ buffers, the key binding for the command
|
||
src_emacs-lisp{(call-interactively 'consult-bookmark)} is {{{kbd(C-x r b)}}}.
|
||
|
||
#+caption[Ensure "osm" installation]:
|
||
#+caption: Ensure ~osm~ installation.
|
||
#+name: lst:ensure-osm-installation
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(ensure-package-installation 'osm)
|
||
#+end_src
|
||
|
||
#+caption[Using "osm" example]:
|
||
#+caption: Using ~osm~ example.
|
||
#+name: lst:using-osm-example
|
||
#+begin_src emacs-lisp -n :results silent :tangle no
|
||
(osm "Tower of London")
|
||
#+end_src
|
||
|
||
*** [[info:url#Top][URL (info)]] :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:url
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
|
||
[[https://lists.gnu.org/archive/html/emacs-devel/2024-03/msg00670.html][How to retrieve only URL metadata]]
|
||
|
||
#+caption[Make an URL HEAD request]:
|
||
#+caption: Make an URL HEAD request.
|
||
#+name: lst:url-head-request
|
||
#+header: :wrap "src text -n :eval no"
|
||
#+begin_src emacs-lisp -n :eval never-export :exports both
|
||
(with-current-buffer
|
||
(let ((url-request-method "HEAD"))
|
||
(url-retrieve-synchronously "https://www.gnu.org/"))
|
||
(buffer-string))
|
||
#+end_src
|
||
|
||
#+caption[URL HEAD request result]:
|
||
#+caption: URL HEAD request result.
|
||
#+name: lst:url-head-request-result
|
||
#+RESULTS: lst:url-head-request
|
||
#+begin_src text -n :eval no
|
||
HTTP/1.1 200 OK
|
||
Date: Wed, 27 Mar 2024 07:40:07 GMT
|
||
Server: Apache/2.4.29
|
||
Content-Location: home.html
|
||
Vary: negotiate,accept-language,Accept-Encoding
|
||
TCN: choice
|
||
Strict-Transport-Security: max-age=63072000
|
||
X-Frame-Options: sameorigin
|
||
X-Content-Type-Options: nosniff
|
||
Access-Control-Allow-Origin: (null)
|
||
Accept-Ranges: bytes
|
||
Cache-Control: max-age=0
|
||
Expires: Wed, 27 Mar 2024 07:40:07 GMT
|
||
Content-Length: 9803
|
||
Keep-Alive: timeout=5, max=100
|
||
Connection: Keep-Alive
|
||
Content-Type: text/html
|
||
Content-Language: en
|
||
|
||
#+end_src
|
||
|
||
*** [[https://www.emacswiki.org/emacs/WebJump][Webjump]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:webjump
|
||
:END:
|
||
|
||
Listing [[lst:set-webjump-options]] binds {{{kbd(C-c j)}}} to =webjump= and
|
||
sets the =webjump-sites= option.
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Set =webjump= options and bind the =webjump= command]:
|
||
#+caption: Set =webjump= options and bind the =webjump= command.
|
||
#+name: lst:set-webjump-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (fboundp 'webjump)
|
||
(keymap-global-set "C-c j" 'webjump)
|
||
(with-eval-after-load 'webjump
|
||
(setopt
|
||
webjump-sites
|
||
'(("ASCII Table" . "www.ascii-code.com/ASCII")
|
||
("CS 325 AI Programming" . "courses.cs.northwestern.edu/325")
|
||
("Emacs News" . "sachachua.com/blog/category/emacs-news")
|
||
("Mastering Emacs" . "www.masteringemacs.org")
|
||
("Planet Emacs Life" . "planet.emacslife.com")
|
||
("Worg - Org Mode Community" . "orgmode.org/worg")
|
||
("Git: Emacs" . "git.savannah.gnu.org/cgit/emacs.git")
|
||
("Git: Emacs MultiMedia System" .
|
||
"https://git.savannah.gnu.org/cgit/emms.git")
|
||
("Git: GNU AUCTeX" . "git.savannah.gnu.org/cgit/auctex.git")
|
||
("Git: GNU ELPA" . "git.savannah.gnu.org/cgit/emacs/elpa.git")
|
||
("Git: NonGNU ELPA" . "git.savannah.gnu.org/cgit/emacs/nongnu.git")
|
||
("Git: Org Mode" . "git.savannah.gnu.org/cgit/emacs/org-mode.git")
|
||
("List: Org Mode" . "list.orgmode.org")
|
||
("List: Emacs Developer Archives" .
|
||
"lists.gnu.org/archive/html/emacs-devel/")
|
||
("List: Help GNU Emacs Archives" .
|
||
"lists.gnu.org/archive/html/help-gnu-emacs/")
|
||
("Counterpunch" . "www.counterpunch.org")
|
||
("Dictionary FR" . [simple-query "www.cnrtl.fr"
|
||
"www.cnrtl.fr/definition/" ""])
|
||
("Dictionary NL" . [simple-query "www.woorden.org"
|
||
"www.woorden.org/woord/" ""])))))
|
||
#+end_src
|
||
|
||
** [[info:gnus#Top][Reading News and Mail (info)]] :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading-news-mail
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
|
||
Reading news and mail:
|
||
1. [[https://www.maketecheasier.com/emacs-usenet-reader-with-gnus/][How to use Emacs as a USENET reader with Gnus]]
|
||
2. [[https://github.com/redguardtoo/mastering-emacs-in-one-year-guide/blob/master/gnus-guide-en.org][A practical guide to Gnus]]
|
||
3. [[https://jao.io/blog/2021-05-17-reading-and-searching-gmane-with-gnus-fast.html][Fast reading and searching of Gmane.io with Gnus]]
|
||
4. [[https://www.brautaset.org/posts/leafnode-nntp-os-x.html][Setting up Leafnode on macOS]]
|
||
5. [[https://blog.bitside.pl/posts/gnus/][Gnus]]
|
||
6. [[http://www.bobnewell.net/publish/35years/gmailhacks.html][Stupid GMail hacks for Gnus]]
|
||
7. [[http://www.bobnewell.net/publish/35years/gnuhacks.html][More stupid Gnus hacks]]
|
||
8. [[https://config.phundrak.com/emacs.html][See Mu4e section of Phundrak's Emacs configuration]]
|
||
Password management:
|
||
1. [[https://www.passwordstore.org/][Pass: the standard unix password manager]]
|
||
2. [[https://www.howtogeek.com/devops/how-to-use-pass-a-command-line-password-manager-for-linux-systems/][How to use Pass, a command-line password manager for Unix systems]]
|
||
3. [[https://vitalyparnas.com/guides/pass/][Clever uses of pass, the Unix password manager]]
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Gnus key bindings]:
|
||
#+caption: Gnus key bindings.
|
||
#+name: tab:gnus-key-bindings
|
||
| command | map | keys |
|
||
|--------------------------------+---------------------+----------------|
|
||
| gnus-group-list-active | gnus-group-list-map | {{{kbd(A-A)}}} |
|
||
| gnus-group-list-all-groups | gnus-group-mode-map | {{{kbd(L)}}} |
|
||
| gnus-group-toggle-subscription | gnus-group-mode-map | {{{kbd(U)}}} |
|
||
|--------------------------------+---------------------+----------------|
|
||
|
||
#+caption[Configure =gnus=]:
|
||
#+caption: Configure =gnus=.
|
||
#+name: lst:configure-gnus
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'gnus
|
||
(setopt gnus-select-method '(nntp "news.gmane.io")))
|
||
|
||
(with-eval-after-load 'gnus-start
|
||
(setopt gnus-check-bogus-newsgroups nil
|
||
gnus-check-new-newsgroups 'ask-server
|
||
gnus-read-newsrc-file t
|
||
gnus-read-active-file 'some
|
||
gnus-save-killed-list t
|
||
gnus-save-newsrc-file t
|
||
gnus-use-dribble-file t))
|
||
|
||
(with-eval-after-load 'gnus-sum
|
||
(setopt gnus-thread-hide-subtree t))
|
||
#+end_src
|
||
|
||
** [[info:emacs#Sending Mail][Sending Mail (info)]] :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:sending-mail
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
|
||
1. [[https://macowners.club/posts/email-emacs-mu4e-macos/][Email setup in Emacs with Mu4e on macOS]]
|
||
2. [[https://www.bounga.org/tips/2020/05/03/multiple-smtp-accounts-in-gnus-without-external-tools/][Setting up multiple IMAP and SMTP accounts in Gnus]]
|
||
3. [[https://jherrlin.github.io/posts/emacs-on-macos-monterey/][Emacs on Macos Monterey]]
|
||
4. [[https://github.com/00riddle00/dotfiles/blob/master/.msmtprc][Msmtp resource file for posteo.net]]
|
||
5. [[https://www.devdungeon.com/content/gpg-tutorial][GPG tutorial]]
|
||
6. [[https://en.wikipedia.org/wiki/Key_server_(cryptographic)#Keyserver_examples][Cryptographic key server examples]]
|
||
7. [[https://rakhim.org/fastmail-setup-with-emacs-mu4e-and-mbsync-on-macos/][Fastmail setup with Emacs, mu4e and mbsync on macOS]]
|
||
|
||
#+caption[Configure =message=]:
|
||
#+caption: Configure =message=.
|
||
#+name: lst:configure-message
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(setopt user-full-name "Gerard Vermeulen"
|
||
user-mail-address "gerard.vermeulen@posteo.net")
|
||
(with-eval-after-load 'message
|
||
(setopt message-sendmail-envelope-from 'header))
|
||
#+end_src
|
||
|
||
#+caption[Configure =sendmail=]:
|
||
#+caption: Configure =sendmail=.
|
||
#+name: lst:configure-sendmail
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(with-eval-after-load 'sendmail
|
||
(setopt mail-specify-envelope-from t
|
||
mail-envelope-from 'header
|
||
send-mail-function #'sendmail-send-it
|
||
sendmail-program (executable-find "msmtp")))
|
||
#+end_src
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Resource file for =msmtp= on =Darwin=]:
|
||
#+caption: Resource file for =msmtp= on =Darwin=.
|
||
#+name: lst:darwin-msmpt-resource-file
|
||
#+header: :tangle (if (eq 'darwin system-type) "~/.msmtprc" "no")
|
||
#+begin_src conf -n
|
||
# Set default values for all accounts.
|
||
defaults
|
||
auth on
|
||
tls on
|
||
logfile ~/.msmtp.log
|
||
|
||
# CNRS on Darwin
|
||
# security add-generic-password -s cnrs -a gerard.vermeulen@neel.cnrs.fr -w
|
||
account cnrs
|
||
host smtps.grenoble.cnrs.fr
|
||
port 465
|
||
tls_starttls off
|
||
from gerard.vermeulen@neel.cnrs.fr
|
||
user gerard.vermeulen@neel.cnrs.fr
|
||
passwordeval security find-generic-password -s cnrs -w
|
||
|
||
# https://notmuchmail.org/emacstips/#index11h2
|
||
# https://wiki.debian.org/msmtp
|
||
# https://tushartyagi.com/blog/configure-mu4e-and-msmtp/
|
||
# https://www.emacswiki.org/emacs/GnusMSMTP
|
||
# https://www.ying-ish.com/essay/emacs-notmuch-mbsync-msmtp-email/
|
||
|
||
# Local Variables:
|
||
# mode: conf-unix
|
||
# End:
|
||
#+end_src
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Resource file for =msmtp= on =Linux=]:
|
||
#+caption: Resource file for =msmtp= on =Linux=.
|
||
#+name: lst:linux-msmpt-resource-file
|
||
#+header: :tangle (if (eq 'gnu/linux system-type) "~/.msmtprc" "no")
|
||
#+begin_src conf -n
|
||
# Set default values for all accounts.
|
||
defaults
|
||
auth on
|
||
tls on
|
||
tls_trust_file /etc/ssl/certs/ca-certificates.crt
|
||
logfile ~/.msmtp.log
|
||
|
||
# CNRS on Linux
|
||
# secret-tool store --label=msmtp host smtps.grenoble.cnrs.fr service smtp user gerard.vermeulen@neel.cnrs.fr
|
||
account cnrs
|
||
host smtps.grenoble.cnrs.fr
|
||
port 465
|
||
tls_starttls off
|
||
from gerard.vermeulen@neel.cnrs.fr
|
||
user gerard.vermeulen@neel.cnrs.fr
|
||
|
||
# https://notmuchmail.org/emacstips/#index11h2
|
||
# https://wiki.debian.org/msmtp
|
||
# https://tushartyagi.com/blog/configure-mu4e-and-msmtp/
|
||
# https://www.emacswiki.org/emacs/GnusMSMTP
|
||
# https://www.ying-ish.com/essay/emacs-notmuch-mbsync-msmtp-email/
|
||
|
||
# Local Variables:
|
||
# mode: conf-unix
|
||
# End:
|
||
#+end_src
|
||
|
||
** [[https://github.com/skeeto/elfeed#readme][Elfeed: Emacs web feed reader]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-web-feed-reader
|
||
:END:
|
||
|
||
Listing [[lst:set-elfeed-options]] sets =elfeed= options, binds the =elfeed=
|
||
command, and makes a minimal attempt to enable =emms=.
|
||
|
||
#+caption[Set =elfeed= options and bind =elfeed= command]:
|
||
#+caption: Set =elfeed= options and bind =elfeed= command.
|
||
#+name: lst:set-elfeed-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation '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://sachachua.com/blog/category/emacs/feed" s-chua)
|
||
("https://www.lemonde.fr/blog/huet/feed/" sciences))))
|
||
(with-eval-after-load 'elfeed-show
|
||
(when (fboundp 'emms-all)
|
||
(emms-all))))
|
||
#+end_src
|
||
|
||
** [[info:emms#Top][Emacs Multimedia System (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-multimedia-system
|
||
:END:
|
||
|
||
The link [[https://sqrtminusone.xyz/posts/2021-09-07-emms/][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~.
|
||
|
||
#+caption[Set =emms= options]:
|
||
#+caption: Set =emms= options.
|
||
#+name: lst:set-emms-options
|
||
#+begin_src emacs-lisp -n :results silent
|
||
(when (ensure-package-installation 'emms)
|
||
(with-eval-after-load 'emms
|
||
(emms-all) ;; Restrict now to `emms-player-mpd' use only.
|
||
(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-mpv
|
||
(setopt emms-player-mpv-update-metadata t
|
||
emms-player-mpv-parameters
|
||
(append emms-player-mpv-parameters
|
||
'("--ytdl-format=best" "--config=no" "--fullscreen"))))
|
||
(with-eval-after-load 'emms-playing-time
|
||
(setopt emms-playing-time-display-format " %s "))
|
||
(with-eval-after-load 'emms-streams
|
||
(setopt emms-streams-file
|
||
(no-littering-expand-etc-file-name "emms/streams.emms"))))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Init File][Init File (info)]] footer
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:user-init-file-footer
|
||
:END:
|
||
|
||
#+caption[Tangle the =user-init-file= footer]:
|
||
#+caption: Tangle the =user-init-file= footer.
|
||
#+name: lst:tangle-user-init-file-footer
|
||
#+begin_src emacs-lisp -n :results silent
|
||
;; Emacs looks for "Local variables:" after the last "form-feed".
|
||
|
||
;; Local Variables:
|
||
;; eval: (bug-reference-mode +1)
|
||
;; indent-tabs-mode: nil
|
||
;; End:
|
||
;;; init.el ends here
|
||
#+end_src
|
||
|
||
* Local variables linking to [[#sec:latexmk-save-compile-display-loop][Latexmk save-compile-display-loop]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:local-variables
|
||
:END:
|
||
|
||
Only the [[info:org#Top][Org]] source file shows the local variables footer.
|
||
|
||
#+print_bibliography:
|
||
|
||
# Emacs looks for "Local variables:" after the last "form-feed".
|
||
|
||
# Local Variables:
|
||
# bug-reference-mode: t
|
||
# compile-command: "latexmk -interaction=nonstopmode -lualatex -pvc -shell-escape README.tex"
|
||
# fill-column: 80
|
||
# org-edit-src-content-indentation: 0
|
||
# org-latex-src-block-backend: engraved
|
||
# eval: (org-eval-emacs-lisp-setup-blocks)
|
||
# End:
|