d4b4b7c188
* Adhere to https://protesilaos.com/codelog/2022-01-31-learning-emacs. * Uninstall which-key. * Enable embark-prefix-help-command.
2825 lines
121 KiB
Org Mode
2825 lines
121 KiB
Org Mode
#+title: Emacs setup for use with LaTeX, Org, and Python
|
||
#+author: Gerard Vermeulen
|
||
#+latex_class: article
|
||
#+latex_class_options: [11pt,a4paper,english,svgnames,tables]
|
||
#+setupfile: "setup-include.org"
|
||
#+include: "setup-include.org"
|
||
|
||
* Quick start
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:quick-start
|
||
:END:
|
||
|
||
Backup your =user-emacs-directory= (defaults often to =~/.emacs.d= on =Linux=,
|
||
=Unix=, or =Darwin=) directory and execute the commands in listing
|
||
[[lst:prepare-user-emacs-directory]]. After invoking Emacs interactively (in
|
||
interactive mode, neither in batch mode, nor in server mode), Emacs will ask you
|
||
to install a selected set of packages. Quit Emacs and invoke Emacs again.
|
||
|
||
#+caption[Prepare the user-emacs-directory]:
|
||
#+caption: Clone and initialize the user-emacs-directory.
|
||
#+name: lst:prepare-user-emacs-directory
|
||
#+begin_src shell :noeval :tangle no
|
||
cd ~
|
||
git clone ccdr@mercury.grenoble.cnrs.fr:SERVER/emacs.d.git .emacs.d
|
||
make --directory=.emacs.d init
|
||
emacs &
|
||
#+end_src
|
||
|
||
* Introduction
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:introduction
|
||
:END:
|
||
|
||
This Emacs setup aims to install automatically a minimal set of extension
|
||
packages that allows to handle my reports and presentations. The file format of
|
||
the reports is [[https://orgmode.org/][Org Mode]] plain text with [[https://www.python.org/][Python]] source code blocks and the file
|
||
format of the presentations is [[https://www.latex-project.org/][LaTeX]].
|
||
|
||
This [[info:org#Top][org]] file (more precisely the original [[info:org#Top][org]] source file of this file)
|
||
illustrates three methods in my work-flow:
|
||
1. How to tangle (or export) source blocks from [[info:org#Top][org]] files. This file contains
|
||
source blocks to produce the files =early-init.el=, =init.el=, =latexmkrc=, =org-store-link=, and =example.py= by tangling.
|
||
2. How to export [[info:org#Top][org]] files to other formats such as [[https://en.wikipedia.org/wiki/HTML][HTML]], [[https://www.latex-project.org/][LaTeX]], and [[https://en.wikipedia.org/wiki/PDF][PDF]].
|
||
3. How [[info:org#Hyperlinks][org hyperlinks (info)]] allow to link inside and outside [[info:org#Top][Org Mode]]: hover
|
||
over or click on the links to experiment.
|
||
|
||
The [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX - Aalborg University Center TeX]] extension package provides a
|
||
powerful [[https://en.wikipedia.org/wiki/Text-based_user_interface][Text-based User Interface (TUI)]] environment to edit the [[https://www.latex-project.org/][LaTeX]]
|
||
presentations.
|
||
|
||
The [[https://github.com/bdarcus/citar][citar]] extension package provides quick filtering and selecting of
|
||
bibliographic entries, and the option to run different commands on those
|
||
selections. [[https://github.com/bdarcus/citar][Citar]] requires [[info:org#Top][Org-9.5 (info)]], which is already part of Emacs-28.1.
|
||
[[https://github.com/bdarcus/citar][Citar]] exploits the enhancements of Emacs' builtin selection mechanism provided
|
||
by the extension packages [[https://github.com/minad/vertico][vertico]], [[https://github.com/oantolin/orderless][orderless]], [[https://github.com/oantolin/embark][embark]], [[https://github.com/minad/marginalia][marginalia]], and [[https://github.com/minad/consult][consult]].
|
||
The [[https://github.com/andras-simonyi/citeproc-el][citeproc]] extension package provides [[https://citationstyles.org/][CSL: citation style language]] processing
|
||
capabilities to [[https://github.com/bdarcus/citar][citar]] and [[https://orgmode.org/][Org Mode]].
|
||
|
||
The [[https://github.com/vedang/pdf-tools][pdf-tools]] extension package renders [[https://en.wikipedia.org/wiki/PDF][PDF]] file with the possibility to
|
||
annotate the file or to click on anchors in the [[https://en.wikipedia.org/wiki/PDF][PDF]] file that link back to the
|
||
original [[https://www.latex-project.org/][LaTeX]] file of the document. An example of my work-flow are the steps
|
||
to convert this [[info:org#Top][org]] file to [[https://en.wikipedia.org/wiki/PDF][PDF]] and to see the result with [[https://github.com/vedang/pdf-tools][pdf-tools]] in Emacs:
|
||
execute the commands ~pdf-tools-install~, ~org-babel-tangle~,
|
||
~org-latex-export-latex-to-latex~, and ~compile~. This sets up an infinite
|
||
[[https://www.latex-project.org/][LaTeX]] compilation loop to update and redisplay the [[https://en.wikipedia.org/wiki/PDF][PDF]] file after excution of
|
||
the ~org-latex-export-latex-to-latex~ command in this buffer.
|
||
|
||
Here follows a list of interesting Emacs configurations:
|
||
1. [[https://github.com/alhassy/emacs.d][Musa Al-hassy's configuration]] is an impressive example of producing the Emacs
|
||
initialization files and other files by tangling an [[info:org#Top][org]] file. His methodology
|
||
is impressive, as his [[https://alhassy.github.io/ElispCheatSheet/][Elisp Cheat Sheet]] and [[https://alhassy.github.io/org-special-block-extras/][org-special-block-extra package]]
|
||
show. To me, this is a configuration to admire, but his methodology is way
|
||
over my head.
|
||
2. [[https://github.com/oantolin/emacs-config][Omar Antolín Camarena's configuration]] exploits built-in packages, Omar's own
|
||
small packages, and large external packages. Omar is the author of [[https://github.com/oantolin/orderless][orderless]]
|
||
and [[https://github.com/oantolin/embark][embark]]. I have stolen his idea of using ~custom-set-variables~ instead
|
||
of the customize interface although that is [[https://www.masteringemacs.org/article/bad-emacs-advice][bad Emacs advice]] for new users.
|
||
3. [[https://gitlab.com/ambrevar/dotfiles][Pierre Neirhardt's configuration]] implements lazy loading without help of
|
||
external packages. I have stolen his approach of using lazy loading to
|
||
silently ignore the setup stanzas of uninstalled extension packages.
|
||
4. [[https://sachachua.com/dotemacs/][Sacha Chua's configuration]] is a practical example of producing the Emacs
|
||
initialization files by tangling an [[info:org#Top][org]] file. It gives me the impression
|
||
that she is a very practical person trying to achieve her goals by the most
|
||
efficient means. I have stolen her idea of using [[https://github.com/quelpa/quelpa][quelpa]] to install packages
|
||
from any source.
|
||
5. [[https://github.com/purcell/emacs.d][Steve Purcell's configuration]] is well organized, a showcase of readable code,
|
||
as well helpful commit and issue histories. See for instance the discussion
|
||
on [[https://github.com/purcell/emacs.d/issues/778][the correctness of order of company candidates in Emacs lisp mode]].
|
||
|
||
Here follows a list of links on how to use Emacs and Elisp:
|
||
1. [[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.
|
||
2. [[https://www.masteringemacs.org/][Mastering Emacs]] is a link to a blog with many interesting posts that promotes
|
||
a book on how to become a proficient Emacs user.
|
||
3. [[https://endlessparentheses.com/][Endless Parentheses]] is a blog with many mindblowing code snippets.
|
||
|
||
* [[info:emacs#Early Init File][Early Init File (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:early-init-file
|
||
:END:
|
||
|
||
Try to load [[https://github.com/emacscollective/no-littering][no-littering]] as early as possible, since it helps to keep
|
||
=~/.emacs.d= clean.
|
||
|
||
#+caption[Tangle the early-init-file]:
|
||
#+caption: Tangle the early-init-file.
|
||
#+name: lst:tangle-early-init-file
|
||
#+begin_src emacs-lisp :tangle early-init.el
|
||
;;; early-init.el --- user early-init file -*- lexical-binding: t -*-
|
||
;;; Commentary:
|
||
;;; Code:
|
||
(setq load-prefer-newer t)
|
||
|
||
(require 'no-littering nil 'noerror)
|
||
|
||
(provide 'early-init)
|
||
;; Emacs looks for "Local variables:" after the last "?\n?\f".
|
||
|
||
;; Local Variables:
|
||
;; indent-tabs-mode: nil
|
||
;; End:
|
||
;;; earl-init.el ends here
|
||
#+end_src
|
||
|
||
In order to get help in understanding the code block above in a buffer showing
|
||
the original [[info:org#Top][Org]] source file, type {{{kbd(C-c C-c)}}} after moving
|
||
point (or cursor) to one of the items of the list:
|
||
1. src_emacs-lisp{(describe-variable #'load-prefer-newer t)}
|
||
2. src_emacs-lisp{(apropos-library "no-littering")}
|
||
3. src_emacs-lisp{(find-function #'hack-local-variables)}
|
||
to execute the code between the curly braces for access to help. This shows
|
||
that *Emacs is a self-documenting editor.*
|
||
|
||
* [[info:emacs#Init File][Init File (info)]] header
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:init-file-header
|
||
:END:
|
||
|
||
The =user-init-file= header requires =cl-lib= and customizes Emacs variables.
|
||
It consists of three parts in listing [[lst:1st-custom-set-variables-call]],
|
||
[[lst:2nd-custom-set-variables-call]], and [[lst:3rd-custom-set-variables-call]] in
|
||
order to limit the length of the listings for exporting to LaTeX. The [[info:elisp#Quoting][quoting
|
||
(info)]] and the [[info:elisp#Backquote][backquote (info)]] pages explain how to understand the reader
|
||
macros ~'~ (quote), ~`~ (backquote), ~,~ (substitute) and ~@,~ (splice) in the
|
||
~custom-set-variables~ function calls in listing
|
||
[[lst:1st-custom-set-variables-call]], [[lst:2nd-custom-set-variables-call]], and
|
||
[[lst:3rd-custom-set-variables-call]]. A tutorial of how to use those reader macros
|
||
is the [[https://mullikine.github.io/posts/macro-tutorial/][didactic emacs-lisp macro example]]. The [[info:emacs#Init File][init file (info)]] does not load
|
||
the ~custom-file~ as [[info:emacs#Saving Customizations][saving customizations (info)]] recommends because of the
|
||
~custom-set-variables~ function calls.
|
||
|
||
#+caption[Customize the first set of Emacs variables]:
|
||
#+caption: Customize the first set of Emacs variables.
|
||
#+name: lst:1st-custom-set-variables-call
|
||
#+begin_src emacs-lisp
|
||
;;; init.el --- user init file -*- lexical-binding: t -*-
|
||
;;; Commentary:
|
||
;;; Code:
|
||
(require 'cl-lib)
|
||
|
||
(custom-set-variables
|
||
'(after-save-hook #'executable-make-buffer-file-executable-if-script-p)
|
||
'(column-number-mode t)
|
||
'(cursor-type 'box)
|
||
`(custom-file ,(locate-user-emacs-file "custom.el"))
|
||
'(epg-pinentry-mode 'loopback)
|
||
'(global-hl-line-mode t)
|
||
'(global-hl-line-sticky-flag t)
|
||
'(indent-tabs-mode nil)
|
||
'(inhibit-startup-buffer-menu t)
|
||
'(inhibit-startup-screen t)
|
||
'(initial-buffer-choice t)
|
||
'(initial-scratch-message "")
|
||
`(insert-directory-program ,(or (executable-find "gls")
|
||
(executable-find "ls")))
|
||
'(kill-ring-max 300)
|
||
'(package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
|
||
("nongnu" . "https://elpa.nongnu.org/nongnu/")
|
||
("melpa" . "https://melpa.org/packages/")))
|
||
;; Pin those packages to GNU ELPA to get the info documentation.
|
||
'(package-pinned-packages '((consult . "gnu")
|
||
(marginalia . "gnu")
|
||
(vertico . "gnu"))))
|
||
#+end_src
|
||
|
||
#+caption[Customize the second set of Emacs variables]:
|
||
#+caption: Customize the second set of Emacs variables.
|
||
#+name: lst:2nd-custom-set-variables-call
|
||
#+begin_src emacs-lisp
|
||
(custom-set-variables
|
||
`(package-selected-packages
|
||
`(,@(when (version< emacs-version "28.0")
|
||
'(org ; plain text thought organizer
|
||
python)) ; mode to edit Python code
|
||
anaconda-mode ; strangles python-mode
|
||
applescript-mode ; mode to edit AppleScript code
|
||
async ; asynchroneous processing
|
||
auctex ; Aalborg University Center TeX
|
||
blacken ; Black Python code formatter client
|
||
citar ; bibliography handling
|
||
citeproc ; bibliography handling
|
||
company ; complete anything
|
||
company-anaconda ; complete anything in anaconda-mode
|
||
consult ; consult completing-read
|
||
eglot ; Emacs polyGLOT LSP client
|
||
electric-operator ; automatic spacing around operators
|
||
elfeed ; web feed reader
|
||
embark ; act on any buffer selection
|
||
emms ; Emacs Multi-Media System
|
||
htmlize ; convert buffer contents to HTML
|
||
iedit ; simultaneous multi-entity editing
|
||
keycast ; show current command with binding
|
||
magit ; Git Text-based User Interface
|
||
marginalia ; minibuffer margin notes
|
||
markdown-mode ; markdown text mode
|
||
no-littering ; keep `user-emacs-directory' clean
|
||
nov ; EPUB reader
|
||
orderless ; Emacs completion style
|
||
pdf-tools ; interactive docview replacement
|
||
pdf-view-restore ; add view history to pdf-tools
|
||
pyenv-mode ; Python environment selector
|
||
quelpa ; install Emacs packages from source
|
||
rainbow-mode ; set background color to color string
|
||
sly ; Sylvester the Cat's Common Lisp IDE
|
||
smartparens ; smart editing of character pairs
|
||
toml-mode ; Tom's Obvious Minimal Language mode
|
||
undo-tree ; more advanced yet simpler undo system
|
||
vertico ; VERTical Interactive Completion
|
||
wgrep ; open a writable grep buffer
|
||
wordnut ; WordNet lexical database
|
||
writegood-mode ; bullshit and weasel-word detector
|
||
ws-butler ; remove trailing whitespace
|
||
xr ; undo rx to grok regular expressions
|
||
yasnippet))) ; code or text template expansion
|
||
#+end_src
|
||
|
||
#+caption[Customize the third set of Emacs variables]:
|
||
#+caption: Customize the third set of Emacs variables.
|
||
#+name: lst:3rd-custom-set-variables-call
|
||
#+begin_src emacs-lisp
|
||
(custom-set-variables
|
||
'(recentf-mode t)
|
||
'(save-place-mode t)
|
||
'(scroll-bar-mode nil)
|
||
'(tab-always-indent 'complete)
|
||
'(tab-width 8)
|
||
'(tool-bar-mode nil)
|
||
'(url-cookie-trusted-urls nil)
|
||
'(url-cookie-untrusted-urls '(".*"))
|
||
'(use-dialog-box nil)
|
||
'(use-short-answer t)
|
||
'(view-read-only t))
|
||
|
||
(when (eq system-type 'darwin)
|
||
(custom-set-variables
|
||
'(ns-alternate-modifier nil)
|
||
'(ns-command-modifier 'meta)
|
||
'(ns-right-command-modifier 'super)))
|
||
|
||
(when (eq window-system 'ns)
|
||
(add-to-list 'initial-frame-alist '(height . 51))
|
||
(add-to-list 'initial-frame-alist '(width . 180)))
|
||
#+end_src
|
||
|
||
** [[info:emacs#Faces][Text faces or look (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:text-faces-or-look
|
||
:END:
|
||
|
||
Section [[#sec:text-faces-or-styles][text faces or styles (info)]] tells that this setup does not use theming.
|
||
However, in order to improve visibility, it relies on:
|
||
1. Tweaking faces in memory after loading as in listing
|
||
[[lst:tweak-faces-faces-after]].
|
||
2. Shadowing the definition of faces before loading as in listing
|
||
[[lst:tweak-org-faces-before]] and [[lst:tweak-sh-script-faces-before]]. 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.
|
||
Section [[#sec:text-faces-or-styles][text faces or styles (info)]] also defines the function
|
||
src_emacs-lisp{(my-invert-default-face)} to toggle between a dark and a light
|
||
background.
|
||
|
||
#+caption[Improve the visibility of faces in =faces.el=]:
|
||
#+caption: Improve the visibility of faces in =faces.el=.
|
||
#+name: lst:tweak-faces-faces-after
|
||
#+begin_src emacs-lisp
|
||
(defun tweak-region-face-background-color ()
|
||
(when (featurep 'gtk)
|
||
(set-face-attribute
|
||
'region nil
|
||
:background (cdr (assoc (face-attribute 'default :background)
|
||
'(("white" . "LightGoldenrod2")
|
||
("black" . "blue3")))))))
|
||
|
||
(tweak-region-face-background-color)
|
||
#+end_src
|
||
|
||
#+caption[Improve the visibility of faces in =org-faces.el=]:
|
||
#+caption: Improve the visibility of faces in =org-faces.el=.
|
||
#+name: lst:tweak-org-faces-before
|
||
#+begin_src emacs-lisp
|
||
;; Shadow the definition in org-faces.el:
|
||
(defface org-block
|
||
;; https://emacs.stackexchange.com/questions/9600/
|
||
;; how-can-i-override-a-pre-defined-face-for-light-and-dark-backgrounds
|
||
`((((background dark))
|
||
:inherit (fixed-pitch)
|
||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||
:background "#444444")
|
||
(t
|
||
:inherit (fixed-pitch)
|
||
,@(and (>= emacs-major-version 27) '(:extend t))
|
||
:background "#FFFFD0"))
|
||
"My face used for text inside various blocks.
|
||
|
||
It is always used for source blocks. You can refine what face
|
||
should be used depending on the source block language by setting,
|
||
`org-src-block-faces', which takes precedence.
|
||
|
||
When `org-fontify-quote-and-verse-blocks' is not nil, text inside
|
||
verse and quote blocks are fontified using the `org-verse' and
|
||
`org-quote' faces, which inherit from `org-block'.")
|
||
#+end_src
|
||
|
||
#+caption[Improve the visibility of faces in =sh-script.el=]:
|
||
#+caption: Improve the visibility of faces in =sh-script.el=.
|
||
#+name: lst:tweak-sh-script-faces-before
|
||
#+begin_src emacs-lisp
|
||
;; Shadow the definition in sh-script.el:
|
||
(defface sh-heredoc
|
||
'((((class color) (background dark))
|
||
(:foreground "yellow"))
|
||
(((class color) (background light))
|
||
(:foreground "magenta"))
|
||
(t
|
||
(:weight bold)))
|
||
"My face to show a here-document.")
|
||
#+end_src
|
||
|
||
* [[info:emacs#Package Installation][Install the selected packages (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:install-selected-packages
|
||
:END:
|
||
|
||
[[info:emacs#Package Installation][Emacs installs packages]] from archives on the internet. This setup uses three
|
||
archives:
|
||
1. The [[https://elpa.gnu.org/][GNU Emacs Lisp Package Archive]]
|
||
2. The [[https://elpa.nongnu.org/][NonGNU Emacs Lisp Package Archive]].
|
||
3. The [[https://melpa.org/#/][Milkypostman’s Emacs Lisp Package Archive (MELPA)]].
|
||
In addition, the [[https://github.com/quelpa/quelpa][quelpa]] tool allows to fetch code from any source and build a
|
||
package on your computer before installation. The code in listing
|
||
[[lst:install-selected-packages]] assumes that the package system is in a *virgin*
|
||
state if the package [[https://github.com/emacscollective/no-littering][no-littering]] is not present:
|
||
1. It installs and loads [[https://github.com/emacscollective/no-littering][no-littering]] after ensuring refreshing of the
|
||
contents of available packages.
|
||
2. It ensures installation of [[https://github.com/quelpa/quelpa][quelpa]] before ensuring installation of [[https://www.reddit.com/r/emacs/comments/l6rcqh/undotree_repositorys_new_home_gitlab/][undo-tree]].
|
||
3. It calls src_emacs-lisp{(package-install-selected-packages)} to check the
|
||
installation status of all packages in
|
||
src_emacs-lisp{package-selected-packages} and to install the missing packages
|
||
after the user has agreed to its prompt.
|
||
In case of normal Emacs usage, src_emacs-lisp{(package-list-packages)} refreshes
|
||
the contents of packages and allows to update packages to the latest version.
|
||
|
||
#+caption[Install the selected packages]:
|
||
#+caption: Install the selected packages.
|
||
#+name: lst:install-selected-packages
|
||
#+begin_src emacs-lisp
|
||
(unless (require 'no-littering nil 'noerror)
|
||
(package-refresh-contents)
|
||
(package-install 'no-littering)
|
||
(require 'no-littering))
|
||
|
||
(unless (package-installed-p 'quelpa)
|
||
(package-install 'quelpa))
|
||
|
||
(unless (package-installed-p 'undo-tree)
|
||
;; Neither GNU ELPA, nor MELPA have the latest version.
|
||
(quelpa '(undo-tree :repo "tsc25/undo-tree" :fetcher gitlab)))
|
||
|
||
(unless noninteractive
|
||
(package-install-selected-packages))
|
||
#+end_src
|
||
|
||
* [[info:dir#Top][Info documentation]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:info-documentation
|
||
:END:
|
||
|
||
Listing [[lst:configure-info]] fixes what looks like a bug in Emacs-28.0.91 and adds
|
||
a path in my home directory to the places where =info= looks for files.
|
||
|
||
#+caption[Configure =info=]:
|
||
#+caption: Configure =info=.
|
||
#+name: lst:configure-info
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'info
|
||
(unless (version< emacs-version "28.0")
|
||
;; Why is this necessary after `package-activate-1'?
|
||
(dolist (item package-alist)
|
||
(let ((pkg-dir (package-desc-dir (cadr item))))
|
||
(when (file-exists-p (expand-file-name "dir" pkg-dir))
|
||
(cl-pushnew pkg-dir Info-directory-list :test #'equal)))))
|
||
|
||
;; Emacs should find my "python.info" file.
|
||
(add-to-list 'Info-directory-list
|
||
(expand-file-name "~/.local/share/info")))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Key Bindings][Key bindings (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:key-bindings
|
||
:END:
|
||
** [[info:emacs#Disabling][Disabling Commands (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:enable-disabled-commands
|
||
:END:
|
||
|
||
Execute src_emacs-lisp{(find-library "novice")} to see how Emacs prevents new
|
||
users from shooting themselves in the feet. Listing
|
||
[[lst:configure-disabled-command-function]] enables [[https://www.emacswiki.org/emacs/DisabledCommands][disabled commands on the fly]].
|
||
|
||
#+caption[Configure the =disabled-command-function=]:
|
||
#+caption: Configure the =disabled-command-function=.
|
||
#+name: lst:configure-disabled-command-function
|
||
#+begin_src emacs-lisp
|
||
(setq disabled-command-function
|
||
(defun my-enable-this-command (&rest _args)
|
||
"Called when a disabled command is executed.
|
||
Enable it and re-execute it."
|
||
(put this-command 'disabled nil)
|
||
(message "You typed %s. %s was disabled until now."
|
||
(key-description (this-command-keys)) this-command)
|
||
(sit-for 0)
|
||
(call-interactively this-command)))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Emacs Server][Using Emacs as a server (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:using-emacs-server
|
||
:END:
|
||
|
||
Emacs can act as a server that listens to a socket to share its state (for
|
||
instance buffers and command history) with other programs by means of a shell
|
||
command =emacsclient=. Section [[#sec:latexmk-save-compile-display-loop]] and
|
||
[[#sec:qutebrowser-userscript]] show how to use ~emacsclient~ to:
|
||
1. Install an asynchronous (or background) loop of saving a LaTeX file,
|
||
compiling it, and redisplaying the output in Emacs.
|
||
2. Make [[https://qutebrowser.org][qutebrowser]] send html links with document titles to Emacs.
|
||
The code in listing [[lst:start-emacs-server]] starts the Emacs server.
|
||
|
||
#+caption[Start the Emacs server]:
|
||
#+caption: Start the Emacs server.
|
||
#+name: lst:start-emacs-server
|
||
#+begin_src emacs-lisp
|
||
(when window-system
|
||
(unless (or noninteractive (daemonp))
|
||
(add-hook 'after-init-hook #'server-start)))
|
||
#+end_src
|
||
|
||
** Latexmk save-compile-display loop
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:latexmk-save-compile-display-loop
|
||
:END:
|
||
|
||
The =latexmk= resource file in the next source code block shows how to use
|
||
=emacsclient= to (re)display the PDF file in Emacs after each succesful
|
||
(re)compilation on condition that the settings of the ~compile-command~ local
|
||
variable in section are compatible. The local variable ~compile-command~ in the
|
||
[[#sec:local-variables][local variables]] section (only visible in =org= files, but not in =html= and
|
||
=pdf= files) shows how to use the =latexmkrc= file.
|
||
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Tangle the Latexmk resource file]:
|
||
#+caption: Tangle the Latexmk resource file.
|
||
#+name: lst:latexmkrc
|
||
#+begin_src perl :tangle latexmkrc :comments none
|
||
# pdf previewer and update pdf previewer
|
||
$pdf_previewer = "emacsclient -e '(find-file-other-window %S)'";
|
||
$pdf_update_method = 4; # 4 runs a command to force the update
|
||
$pdf_update_command = "emacsclient -e '(with-current-buffer (find-buffer-visiting %S) (pdf-view-revert-buffer nil t))'";
|
||
# see for instance glossary.latexmkrc
|
||
add_cus_dep( 'acn', 'acr', 0, 'makeglossaries' );
|
||
add_cus_dep( 'glo', 'gls', 0, 'makeglossaries' );
|
||
$clean_ext .= " acr acn alg bbl glo gls glg ist lol run.xml";
|
||
sub makeglossaries {
|
||
my ($name, $path) = fileparse( $$Psource );
|
||
return system "makeglossaries -d '$path' '$name'";
|
||
}
|
||
# Emacs looks for "Local variables:" after the last "?\n?\f".
|
||
|
||
# Local Variables:
|
||
# mode: perl
|
||
# End:
|
||
#+end_src
|
||
|
||
** [[https://qutebrowser.org/doc/userscripts.html][Qutebrowser userscript]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:qutebrowser-userscript
|
||
:END:
|
||
|
||
The next block contains an userscript that sends a [[info:org#The store-link protocol][store-link org-protocol]]
|
||
message with the url and the title from [[https://qutebrowser.org][qutebrowser]] to =emacsclient=. The
|
||
function =urlencode= translates the url and the title for the message. The
|
||
[[info:python#Examples<22>][Python urllib examples]] show how to use =urlencode=. The final =execvp= call
|
||
deals with a [[https://qutebrowser.org][qutebrowser]] userscript requirement: the =emacsclient= process must
|
||
get the PID of the userscript that must kill itself after the take-over.
|
||
Termination of the =emacsclient= process hands control back to [[https://qutebrowser.org][qutebrowser]].
|
||
|
||
On a [[https://en.wikipedia.org/wiki/POSIX][POSIX]] system, you can run the userscript from [[https://qutebrowser.org][qutebrowser]] or from a
|
||
terminal to see whether it works. In case you try to run it from Emacs, Emacs
|
||
may hang or die.
|
||
|
||
#+caption[Tangle a qutebrowser userscript]:
|
||
#+caption: Tangle a qutebrowser userscript.
|
||
#+header: :comments none
|
||
#+header: :tangle-mode (identity #o755)
|
||
#+name: lst:qutebrowser-userscript
|
||
#+begin_src python :noeval :tangle org-store-link
|
||
#!/usr/bin/env python
|
||
from urllib.parse import urlencode
|
||
from os import environ, execvp
|
||
|
||
url = environ.get("QUTE_URL", "https://orgmode.org")
|
||
title = environ.get("QUTE_TITLE", "Org Mode")
|
||
parameters = urlencode({"url": url, "title": title})
|
||
print(payload := f"org-protocol://store-link?{parameters}")
|
||
execvp("emacsclient", ("-n", payload))
|
||
#+end_src
|
||
|
||
** TODO Look into: org-protocol handling with other browser on Darwin
|
||
1. [[http://rwx.io/posts/osx-uri-protocol-handler/][Patrick Goddi: macOS URI protocol handler]] and his [[https://github.com/fooqri/uri-handler][URI-Handler]].
|
||
2. [[https://www.hammerspoon.org/][Hammerspoon]]
|
||
3. [[https://vritser.github.io/posts/capture-anywhere/][Emacs Capture Anywhere]]
|
||
|
||
* Completion
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:completion
|
||
:END:
|
||
|
||
[[info:vertico#Top][Vertico (info)]] provides a performant and minimalistic vertical completion UI
|
||
based on the default completion system and behaves therefore correctly under all
|
||
circumstances. [[https://cestlaz.github.io/post/using-emacs-80-vertico/][Using Vertico, Marginalia, Consult, and Embark]] links to a video
|
||
demonstration. Vertico integrates well with fully supported complementary
|
||
packages to enrich the completion UI:
|
||
1. [[info:orderless#Top][Orderless (info)]] for an advanced completion style,
|
||
2. [[info:embark#Top][Embark (info)]] for minibuffer actions with context menus,
|
||
3. [[info:marginalia#Top][Marginalia (info)]] for rich annotations in the minibuffer, and
|
||
4. [[info:consult#Top][Consult (info)]] for useful search and navigation commands,
|
||
where the order is that of [[https://github.com/bdarcus/citar#installation][enhancing citar's experience]] and the configuration
|
||
steps below.
|
||
|
||
Finally, [[https://company-mode.github.io/][company: a modular complete-anything framework for Emacs]] provides
|
||
completion in any buffer and [[#sec:minibuffer-history-completion][minibuffer-history-completion]] provides completion
|
||
on previous input in the minibuffer.
|
||
|
||
** [[info:vertico#Top][Vertico (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:vertico-configuration
|
||
:END:
|
||
|
||
Listing [[lst:enable-vertico-mode]] configures and enables =savehist-mode= and
|
||
enables =vertico-mode=. The documentation src_emacs-lisp{(describe-function
|
||
'savehist-mode)} why it is best to turn on =savehist-mode= in the Emacs init
|
||
file.
|
||
|
||
#+caption[Enable =savehist-mode= and =vertico-mode=]:
|
||
#+caption: Enable =savehist-mode= and =vertico-mode=.
|
||
#+name: lst:enable-vertico-mode
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(custom-set-variables
|
||
'(history-delete-duplicates t)
|
||
'(history-length 500)
|
||
'(savehist-additional-variables
|
||
'(eww-history
|
||
kill-ring
|
||
regexp-search-string
|
||
search-ring
|
||
search-string)))
|
||
(savehist-mode +1)
|
||
(when (fboundp 'vertico-mode)
|
||
(vertico-mode +1))
|
||
(with-eval-after-load 'vertico
|
||
(define-key vertico-map (kbd "RET") #'vertico-directory-enter)
|
||
(define-key vertico-map (kbd "DEL") #'vertico-directory-delete-char)
|
||
(define-key vertico-map (kbd "M-DEL") #'vertico-directory-delete-word)))
|
||
#+end_src
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Vertico key map bindings]:
|
||
#+caption: Vertico key map bindings.
|
||
#+name: tab:vertico-keymap-bindings
|
||
|---------------------------------+---------------------+------------------------------------|
|
||
| command | keys | remap |
|
||
|---------------------------------+---------------------+------------------------------------|
|
||
| =vertico-directory-delete-char= | {{{kbd(DEL)}}} | |
|
||
| =vertico-directory-delete-word= | {{{kbd(M-DEL)}}} | |
|
||
| =vertico-directory-enter= | {{{kbd(RET)}}} | |
|
||
| =vertico-exit= | {{{kbd(C-j)}}} | =exit-minibuffer= |
|
||
| =vertico-exit-input= | {{{kbd(C-RET)}}} | |
|
||
| =vertico-first= | {{{kbd(M-<)}}} | =beginning-of-buffer= |
|
||
| =vertico-first= | {{{kbd(M-<)}}} | =minibuffer-beginning-of-buffer= |
|
||
| =vertico-insert= | {{{kbd(TAB)}}} | |
|
||
| =vertico-last= | {{{kbd(M->)}}} | =end-of-buffer= |
|
||
| =vertico-next-group= | {{{kbd(C-<down>)}}} | =forward-paragraph= |
|
||
| =vertico-next= | {{{kbd(<down>)}}} | =next-line-or-history-element= |
|
||
| =vertico-next= | {{{kbd(C-n)}}} | =next-line= |
|
||
| =vertico-previous-group= | {{{kbd(C-<up>)}}} | =backward-paragraph= |
|
||
| =vertico-previous= | {{{kbd(<up>)}}} | =previous-line-or-history-element= |
|
||
| =vertico-previous= | {{{kbd(C-p)}}} | =previous-line= |
|
||
| =vertico-save= | {{{kbd(M-w)}}} | =kill-ring-save= |
|
||
| =vertico-scroll-down= | {{{kbd(M-v)}}} | =scroll-down-command= |
|
||
| =vertico-scroll-up= | {{{kbd(C-v)}}} | =scroll-up-command= |
|
||
|---------------------------------+---------------------+------------------------------------|
|
||
|
||
** [[info:orderless#Top][Orderless (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:orderless-configuration
|
||
:END:
|
||
|
||
Listing [[lst:configure-orderless]] enables =orderless=.
|
||
|
||
#+caption[Configure =orderless=]:
|
||
#+caption: Configure =orderless=.
|
||
#+name: lst:configure-orderless
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (fboundp 'orderless-filter)
|
||
(custom-set-variables
|
||
;; https://github.com/purcell/emacs.d/issues/778
|
||
'(completion-styles '(basic completion-partial orderless))
|
||
'(completion-category-defaults nil)
|
||
'(completion-category-overrides
|
||
'((file (styles partial-completion)))))
|
||
(add-hook 'minibuffer-setup-hook
|
||
(defun my-on-minibuffer-setup-hook()
|
||
(setq-default completion-styles '(substring orderless))))))
|
||
#+end_src
|
||
|
||
** [[info:embark#Top][Embark (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:embark-configuration
|
||
:END:
|
||
|
||
Listing [[lst:configure-embark]] configures =embark= key bindings: type for instance
|
||
{{{kbd(C-x C-h)}}} or {{{kbd(C-c C-h)}}} that the initialization
|
||
src_emacs-lisp{(setq prefix-help-command #'embark-prefix-help-command)} enables
|
||
minibuffer help after a prefix key.
|
||
|
||
#+caption[Configure =embark=]:
|
||
#+caption: Configure =embark=.
|
||
#+name: lst:configure-embark
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (cl-every #'fboundp '(embark-act embark-bindings embark-dwim))
|
||
(global-set-key (kbd "C-,") #'embark-act)
|
||
(global-set-key (kbd "C-:") #'embark-dwim)
|
||
(global-set-key (kbd "C-h B") #'embark-bindings)
|
||
(setq prefix-help-command #'embark-prefix-help-command)))
|
||
#+end_src
|
||
|
||
** [[info:marginalia#Top][Marginalia (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:marginalia-configuration
|
||
:END:
|
||
|
||
Listing [[lst:enable-marginalia-mode]] enables =marginalia-mode=.
|
||
|
||
#+caption[Enable =marginalia-mode=]:
|
||
#+caption: Enable =marginalia-mode=.
|
||
#+name: lst:enable-marginalia-mode
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (fboundp 'marginalia-mode)
|
||
(marginalia-mode +1)))
|
||
#+end_src
|
||
|
||
** [[info:consult#Top][Consult (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:consult-configuration
|
||
:END:
|
||
|
||
Listing [[lst:configure-consult]] configures =consult=.
|
||
|
||
#+attr_latex: :booktabs yes :float table
|
||
#+caption[Configuration specific key bindings]:
|
||
#+caption: Configuration specific key-bindings.
|
||
#+name: tab:configuration-specific-key-bindings
|
||
|-------------------------------+---------------------+------------------------|
|
||
| command | keys | key map |
|
||
|-------------------------------+---------------------+------------------------|
|
||
| =consult-apropos= | {{{kbd(<help> a)}}} | =global-map= |
|
||
| =consult-bookmark= | {{{kbd(C-x r b)}}} | =ctl-x-keymap= |
|
||
| =consult-buffer-other-frame= | {{{kbd(C-x 5 b)}}} | =ctl-x-keymap= |
|
||
| =consult-buffer-other-window= | {{{kbd(C-x 4 b)}}} | =ctl-x-keymap= |
|
||
| =consult-buffer= | {{{kbd(C-x b)}}} | =ctl-x-keymap= |
|
||
| =consult-compile-error= | {{{kbd(M-g e)}}} | =goto-map= |
|
||
| =consult-complex-command= | {{{kbd(C-x M-:)}}} | =ctl-x-keymap= |
|
||
| =consult-find= | {{{kbd(M-s f)}}} | =search-map= |
|
||
| =consult-focus-lines= | {{{kbd(M-s u)}}} | =search-map= |
|
||
| =consult-git-grep= | {{{kbd(M-s g)}}} | =search-map= |
|
||
| =consult-global-mark= | {{{kbd(M-g k)}}} | =goto-map= |
|
||
| =consult-goto-line= | {{{kbd(M-g M-g)}}} | =goto-map= |
|
||
| =consult-goto-line= | {{{kbd(M-g g)}}} | =goto-map= |
|
||
| =consult-history= | {{{kbd(C-c h)}}} | =global-map= |
|
||
| =consult-imenu-project= | {{{kbd(M-g i)}}} | =goto-map= |
|
||
| =consult-keep-lines= | {{{kbd(M-s k)}}} | =search-map= |
|
||
| =consult-line= | {{{kbd(M-s l)}}} | =search-map= |
|
||
| =consult-mark= | {{{kbd(M-g m)}}} | =goto-map= |
|
||
| =consult-mode-command= | {{{kbd(C-c m)}}} | =global-map= |
|
||
| =consult-multi-occur= | {{{kbd(M-s m)}}} | =search-map= |
|
||
| =consult-outline= | {{{kbd(M-g o)}}} | =goto-map= |
|
||
| =consult-register= | {{{kbd(C-x r x)}}} | =ctl-x-keymap= |
|
||
| =consult-yank-pop= | {{{kbd(M-y)}}} | =global-map= |
|
||
|-------------------------------+---------------------+------------------------|
|
||
| =elfeed= | {{{kbd(C-x w)}}} | =global-map= |
|
||
| =embark-act= | {{{kbd(C-\,)}}} | =global-map= |
|
||
| =embark-bindings= | {{{kbd(C-h B)}}} | =global-map= |
|
||
| =embark-dwim= | {{{kbd(C-:)}}} | =global-map= |
|
||
| =iedit-mode= | {{{kbd(C-;)}}} | =global-map= |
|
||
| =minibuffer-complete-history= | {{{kbd(C-<tab>)}}} | =minibuffer-local-map= |
|
||
| =narrow-or-widen-dwim= | {{{kbd(C-x C-n)}}} | =ctl-x-keymap= |
|
||
| =org-agenda= | {{{kbd(C-c a)}}} | =global-map= |
|
||
| =org-capture= | {{{kbd(C-c c)}}} | =global-map= |
|
||
| =org-cite= | {{{kbd(C-c b)}}} | =org-mode-map= |
|
||
| =org-insert-link-global= | {{{kbd(C-c C-l)}}} | =global-map= |
|
||
| =org-narrow-to-table= | {{{kbd(C-x n t)}}} | =ctl-x-keymap= |
|
||
| =org-store-link= | {{{kbd(C-c l)}}} | =global-map= |
|
||
|-------------------------------+---------------------+------------------------|
|
||
|
||
|
||
#+caption[Configure =consult=]:
|
||
#+caption: Configure =consult=.
|
||
#+name: lst:configure-consult
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (fboundp 'consult-apropos)
|
||
(custom-set-variables
|
||
'(consult-project-root-function #'vc-root-dir))
|
||
;; C-c bindings (mode-specific-map)
|
||
(global-set-key (kbd "C-c h") #'consult-history)
|
||
(global-set-key (kbd "C-c m") #'consult-mode-command)
|
||
;; C-x bindings (ctl-x-map)
|
||
(define-key ctl-x-map (kbd "M-:") #'consult-complex-command)
|
||
(define-key ctl-x-map (kbd "b") #'consult-buffer)
|
||
(define-key ctl-x-map (kbd "4 b") #'consult-buffer-other-window)
|
||
(define-key ctl-x-map (kbd "5 b") #'consult-buffer-other-frame)
|
||
(define-key ctl-x-map (kbd "r x") #'consult-register)
|
||
(define-key ctl-x-map (kbd "r b") #'consult-bookmark)
|
||
;; M-g bindings (goto-map)
|
||
(define-key goto-map (kbd "g") #'consult-goto-line)
|
||
(define-key goto-map (kbd "M-g") #'consult-goto-line)
|
||
(define-key goto-map (kbd "o") #'consult-outline)
|
||
(define-key goto-map (kbd "m") #'consult-mark)
|
||
(define-key goto-map (kbd "k") #'consult-global-mark)
|
||
(define-key goto-map (kbd "i") #'consult-imenu-project)
|
||
(define-key goto-map (kbd "e") #'consult-compile-error)
|
||
;; M-s bindings (search-map)
|
||
(define-key search-map (kbd "g") #'consult-git-grep)
|
||
(define-key search-map (kbd "f") #'consult-find)
|
||
(define-key search-map (kbd "k") #'consult-keep-lines)
|
||
(define-key search-map (kbd "l") #'consult-line)
|
||
(define-key search-map (kbd "m") #'consult-multi-occur)
|
||
(define-key search-map (kbd "u") #'consult-focus-lines)
|
||
;; Other bindings
|
||
(global-set-key (kbd "M-y") #'consult-yank-pop)
|
||
(global-set-key (kbd "<help> a") #'consult-apropos)
|
||
;; Tweak functions
|
||
(advice-add 'completing-read-multiple
|
||
:override #'consult-completing-read-multiple)
|
||
(fset 'multi-occur #'consult-multi-occur)))
|
||
#+end_src
|
||
|
||
** [[https://company-mode.github.io/][Company: a modular complete anything framework for Emacs]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:company-configuration
|
||
:END:
|
||
|
||
Listing [[lst:configure-company]] configures =company=.
|
||
|
||
#+caption[Configure =company=]:
|
||
#+caption: Configure =company=.
|
||
#+name: lst:configure-company
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (fboundp 'company-mode)
|
||
(custom-set-variables
|
||
;; https://github.com/purcell/emacs.d/issues/778
|
||
'(company-transformers '(company-sort-by-occurrence)))
|
||
(dolist (hook '(LaTeX-mode-hook
|
||
org-mode-hook
|
||
emacs-lisp-mode-hook
|
||
lisp-interaction-mode-hook
|
||
lisp-mode-hook
|
||
python-mode-hook
|
||
ielm-mode-hook
|
||
sly-mrepl-mode-hook))
|
||
(add-hook hook #'company-mode))))
|
||
#+end_src
|
||
|
||
** [[https://lists.gnu.org/archive/html/emacs-devel/2021-12/msg00802.html][Minibuffer history completion]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:minibuffer-history-completion
|
||
:END:
|
||
|
||
See [[https://lists.gnu.org/archive/html/emacs-devel/2021-12/msg00802.html][Juri Linkov (Emacs Developer mailing list)]] for how to allow completion on
|
||
previous input in the minibuffer. Listing
|
||
[[lst:enable-minibuffer-history-completion]] enables minibuffer history completion.
|
||
|
||
#+caption[Enable =minibuffer-history-completion=]:
|
||
#+caption: Enable =minibuffer-history-completion=.
|
||
#+name: lst:enable-minibuffer-history-completion
|
||
#+begin_src emacs-lisp
|
||
(defun minibuffer-setup-history-completions ()
|
||
(unless (or minibuffer-completion-table minibuffer-completion-predicate)
|
||
(setq-local minibuffer-completion-table
|
||
(symbol-value minibuffer-history-variable))))
|
||
|
||
(add-hook 'minibuffer-setup-hook 'minibuffer-setup-history-completions)
|
||
|
||
;; Stolen from Emacs-28.1 for Emacs-27.2:
|
||
(unless (fboundp 'minibuffer--completion-prompt-end)
|
||
(defun minibuffer--completion-prompt-end ()
|
||
(let ((end (minibuffer-prompt-end)))
|
||
(if (< (point) end)
|
||
(user-error "Can't complete in prompt")
|
||
end))))
|
||
|
||
;; Adapted from ‘minibuffer-complete’:
|
||
(defun minibuffer-complete-history ()
|
||
"Allow minibuffer completion on previous input."
|
||
(interactive)
|
||
(completion-in-region (minibuffer--completion-prompt-end) (point-max)
|
||
(symbol-value minibuffer-history-variable)
|
||
nil))
|
||
|
||
(define-key minibuffer-local-map (kbd "C-<tab>") #'minibuffer-complete-history)
|
||
#+end_src
|
||
|
||
|
||
* Reading
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading
|
||
:END:
|
||
|
||
** Reading EPUB files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading-epub-files
|
||
:END:
|
||
|
||
Listing [[lst:enable-nov-mode]] enables =nov-mode=.
|
||
|
||
#+caption[Enable =nov-mode=]:
|
||
#+caption: Enable =nov-mode=.
|
||
#+name: lst:enable-nov-mode
|
||
#+begin_src emacs-lisp
|
||
(when (fboundp 'nov-mode)
|
||
(add-to-list 'auto-mode-alist `(,(rx ".epub" eos) . nov-mode)))
|
||
#+end_src
|
||
|
||
** Reading PDF files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:reading-pdf-files
|
||
:END:
|
||
|
||
The [[https://github.com/vedang/pdf-tools][pdf-tools]] package exploits the [[https://github.com/freedesktop/poppler][poppler]] library to render and to let you
|
||
annotate [[https://en.wikipedia.org/wiki/PDF][PDF]] files. It also exploits the [[https://wiki.contextgarden.net/SyncTeX][SyncTeX]] library to link anchors in [[https://en.wikipedia.org/wiki/PDF][PDF]]
|
||
files produced with LaTeX to the original LaTeX sources.
|
||
|
||
In order to use [[https://github.com/vedang/pdf-tools][pdf-tools]], you have to type =M-x pdf-tools-install= after
|
||
installation of [[https://github.com/vedang/pdf-tools][pdf-tools]] from [[https://melpa.org/][MELPA]] or after each update of [[https://github.com/freedesktop/poppler][poppler]] to build or
|
||
rebuild the =epdfinfo= executable that serves the [[https://en.wikipedia.org/wiki/PDF][PDF]] files to Emacs.
|
||
|
||
#+caption[Enable =pdf-tools=]:
|
||
#+caption: Enable =pdf-tools=.
|
||
#+name: lst:enable-pdf-tools
|
||
#+begin_src emacs-lisp
|
||
;; 'pdf-loader-install' is the lazy equivalent of 'pdf-tools-install':
|
||
;; see the README file.
|
||
(when (fboundp 'pdf-loader-install)
|
||
(pdf-loader-install))
|
||
|
||
(with-eval-after-load 'pdf-view
|
||
(when (fboundp 'pdf-view-restore-mode)
|
||
(add-hook 'pdf-view-mode-hook #'pdf-view-restore-mode)))
|
||
#+end_src
|
||
|
||
* Writing
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing
|
||
:END:
|
||
|
||
** Writing LaTeX files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-latex-files
|
||
:END:
|
||
|
||
Loading =tex.el= immediately instead of lazily ensures proper initialization of
|
||
[[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]]. For instance, the ~TeX-master~ safe local variable in the =tex.el=
|
||
elisp library file has no autoload cookie. Without prior loading of =tex.el=,
|
||
Emacs will complain that ~TeX-master~ is no safe local variable in case it reads
|
||
a LaTeX file that sets ~TeX-master~. Listing [[lst:require-auctex]] initializes
|
||
[[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] properly.
|
||
|
||
#+caption[Require =AUCTeX=]:
|
||
#+caption: Require =AUCTeX=.
|
||
#+name: lst:require-auctex
|
||
#+begin_src emacs-lisp
|
||
;; Use `require' to make `TeX-master' a safe local variable.
|
||
(when (require 'tex nil 'noerror)
|
||
(custom-set-variables
|
||
'(TeX-auto-save t)
|
||
'(TeX-install-font-lock #'font-latex-setup)
|
||
'(TeX-parse-self t)))
|
||
#+end_src
|
||
|
||
Although, the LaTeX =biblatex= is in use, listing [[lst:configure-bibtex]]
|
||
configures the Emacs =bibtex= library for the LaTeX =BiBTeX= package to maintain
|
||
backwards compatibility.
|
||
|
||
#+caption[Configure =bibtex=]:
|
||
#+caption: Configure =bibtex=.
|
||
#+name: lst:configure-bibtex
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'bibtex
|
||
(custom-set-variables '(bibtex-dialect 'BibTeX)))
|
||
#+end_src
|
||
|
||
Listing [[lst:configure-font-latex]] disables font scaling of section titles.
|
||
|
||
#+caption[Configure =font-latex=]:
|
||
#+caption: Configure =font-latex=.
|
||
#+name: lst:configure-font-latex
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'font-latex
|
||
(custom-set-variables
|
||
'(font-latex-fontify-sectioning 1.0)))
|
||
#+end_src
|
||
|
||
Listing [[lst:configure-latex]] configures =latex= for a full featured
|
||
=LaTeX-section-command=.
|
||
|
||
#+caption[Configure =latex=]:
|
||
#+caption: Configure =latex=.
|
||
#+name: lst:configure-latex
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'latex
|
||
(custom-set-variables
|
||
'(LaTeX-section-hook '(LaTeX-section-heading
|
||
LaTeX-section-title
|
||
LaTeX-section-toc
|
||
LaTeX-section-section
|
||
LaTeX-section-label))))
|
||
#+end_src
|
||
|
||
Out of the box, [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] does not indent text between square brackets. The code
|
||
in listing [[lst:configure-tex]] corrects this by advising to override
|
||
~TeX-brace-count-line~ with ~my-TeX-brace-count-line~.
|
||
|
||
#+caption[Configure =TeX=]:
|
||
#+caption: Configure =TeX=.
|
||
#+name: lst:configure-tex
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'tex
|
||
(custom-set-variables
|
||
;; Disable `TeX-electric-math' to prevent collisions with `smartparens'.
|
||
'(TeX-electric-math nil))
|
||
|
||
;; https://emacs.stackexchange.com/questions/17396/
|
||
;; indentation-in-square-brackets
|
||
(defun my-TeX-brace-count-line ()
|
||
"Count number of open/closed braces."
|
||
(save-excursion
|
||
(let ((count 0) (limit (line-end-position)) char)
|
||
(while (progn
|
||
(skip-chars-forward "^{}[]\\\\" limit)
|
||
(when (and (< (point) limit) (not (TeX-in-comment)))
|
||
(setq char (char-after))
|
||
(forward-char)
|
||
(cond ((eq char ?\{)
|
||
(setq count (+ count TeX-brace-indent-level)))
|
||
((eq char ?\})
|
||
(setq count (- count TeX-brace-indent-level)))
|
||
((eq char ?\[)
|
||
(setq count (+ count TeX-brace-indent-level)))
|
||
((eq char ?\])
|
||
(setq count (- count TeX-brace-indent-level)))
|
||
((eq char ?\\)
|
||
(when (< (point) limit)
|
||
(forward-char) t))))))
|
||
count)))
|
||
|
||
(advice-add 'TeX-brace-count-line :override #'my-TeX-brace-count-line))
|
||
#+end_src
|
||
|
||
*** TODO Improve the AUCTeX configuration slowly
|
||
[[https://github.com/thisirs/dotemacs/blob/master/lisp/init-auctex.el][AUCTeX setup of an experienced user]]
|
||
|
||
** Writing Org files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:writing-org-files
|
||
:END:
|
||
|
||
*** [[info:org#Activation][Org activation (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:activate-org
|
||
:END:
|
||
|
||
#+caption[Activate =Org=]:
|
||
#+caption: Activate =Org=.
|
||
#+name: lst:activate-org
|
||
#+begin_src emacs-lisp
|
||
;; Inspect:
|
||
;; function with "C-h f"
|
||
;; symbols with "C-h o"
|
||
;; variables with "C-h v"
|
||
|
||
(global-set-key (kbd "C-c a") #'org-agenda)
|
||
(global-set-key (kbd "C-c c") #'org-capture)
|
||
(global-set-key (kbd "C-c l") #'org-store-link)
|
||
(global-set-key (kbd "C-c C-l") #'org-insert-link-global)
|
||
#+end_src
|
||
|
||
*** Org customization
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:customize-org
|
||
:END:
|
||
|
||
The code in listing [[lst:customize-org-babel]], [[lst:customize-org]], and
|
||
[[lst:customize-org-export]] does basic customization of [[https://orgmode.org/][Org mode]] variables.
|
||
|
||
#+caption[Customize =org-babel=]:
|
||
#+caption: Customize =org-babel=.
|
||
#+name: lst:customize-org-babel
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ob-core
|
||
(custom-set-variables
|
||
'(org-confirm-babel-evaluate nil)))
|
||
|
||
(with-eval-after-load 'ob-python
|
||
(custom-set-variables
|
||
'(org-babel-python-command "python -E")))
|
||
|
||
(with-eval-after-load 'ob-latex
|
||
(custom-set-variables
|
||
'(org-babel-latex-preamble
|
||
(lambda (_)
|
||
"\\documentclass[preview]{standalone}\n"))
|
||
'(org-babel-latex-begin-env
|
||
(lambda (_)
|
||
"\\begin{document}\n"))
|
||
'(org-babel-latex-end-env
|
||
(lambda (_)
|
||
"\\end{document}\n"))))
|
||
|
||
(with-eval-after-load 'ob-lisp
|
||
(with-eval-after-load 'sly
|
||
(custom-set-variables
|
||
'(org-babel-lisp-eval-fn #'sly-eval))))
|
||
#+end_src
|
||
|
||
#+caption[Customize =Org=]:
|
||
#+caption: Customize =Org=.
|
||
#+name: lst:customize-org
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'org
|
||
(custom-set-variables
|
||
'(org-babel-load-languages '((C . t)
|
||
(calc . t)
|
||
(dot . t)
|
||
(emacs-lisp . t)
|
||
(eshell . t)
|
||
(fortran . t)
|
||
(gnuplot . t)
|
||
(latex . t)
|
||
(lisp . t)
|
||
(maxima . t)
|
||
(org . t)
|
||
(perl . t)
|
||
(python . t)
|
||
(shell . t)))
|
||
'(org-export-backends '(ascii beamer html icalendar latex odt texinfo))
|
||
'(org-file-apps '((auto-mode . emacs)
|
||
(directory . emacs)
|
||
("\\.mm\\'" . default)
|
||
("\\.x?html?\\'" . default)
|
||
("\\.pdf\\'" . emacs)))
|
||
'(org-modules '(ol-bibtex
|
||
ol-doi
|
||
ol-eww
|
||
ol-info
|
||
org-id
|
||
org-protocol
|
||
org-tempo))
|
||
'(org-structure-template-alist '(("a" . "export ascii")
|
||
("c" . "center")
|
||
("C" . "comment")
|
||
("e" . "example")
|
||
("E" . "export")
|
||
("h" . "export html")
|
||
("l" . "export latex")
|
||
("q" . "quote")
|
||
("s" . "src")
|
||
("p" . "src python :session :async")
|
||
("v" . "verse")))))
|
||
#+end_src
|
||
|
||
#+caption[Configure =org-mode-map=]:
|
||
#+caption: Configure =org-mode-map=.
|
||
#+name: lst:configure-org-mode-map
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'org
|
||
;; From: "Nicolas Richard" <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)))
|
||
|
||
(define-key org-mode-map (kbd "$") #'org-electric-dollar)
|
||
(define-key org-mode-map (kbd "M-q") #'org-fill-paragraph))
|
||
#+end_src
|
||
|
||
#+caption[Customize =org-export=]:
|
||
#+caption: Customize =org-export=.
|
||
#+name: lst:customize-org-export
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ox-latex
|
||
(custom-set-variables
|
||
'(org-latex-compiler "lualatex")
|
||
'(org-latex-hyperref-template "\\hypersetup{
|
||
pdfauthor={%a},
|
||
pdftitle={%t},
|
||
pdfkeywords={%k},
|
||
pdfsubject={%d},
|
||
pdfcreator={%c},
|
||
pdflang={%L},
|
||
citecolor=blue,
|
||
colorlinks=true,
|
||
filecolor=blue,
|
||
hyperfootnotes=false,
|
||
linkcolor=blue,
|
||
unicode=true,
|
||
urlcolor=blue,
|
||
}\n")
|
||
'(org-latex-listings 'minted)
|
||
'(org-latex-minted-langs '((cc "c++")
|
||
(cperl "perl")
|
||
(diff "diff")
|
||
(shell-script "bash")
|
||
(org "text")
|
||
(toml "toml")))
|
||
'(org-latex-minted-options '(("bgcolor" "LightGoldenrodYellow")))
|
||
`(org-latex-logfiles-extensions
|
||
',(cl-union '("lof" "lot") org-latex-logfiles-extensions :test #'equal))
|
||
'(org-latex-prefer-user-labels t)
|
||
'(org-latex-subtitle-separate t)))
|
||
#+end_src
|
||
|
||
*** [[https://github.com/bdarcus/citar][Citar: citing bibliography]] with [[https://orgmode.org/][Org Mode]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:citing-bibliography
|
||
:END:
|
||
|
||
[[https://github.com/bdarcus/citar][Citar]] is a completing-read front-end to browse and act on BibTeX, BibLaTeX, as
|
||
well as CSL JSON bibliographic data with LaTeX, markdown, and org-cite editing
|
||
support. In combination with vertico, orderless, embark, marginalia, and
|
||
consult, [[https://github.com/bdarcus/citar][Citar]] provides quick filtering and selecting of bibliographic entries
|
||
from the minibuffer, as well as the option to run different commands on those
|
||
selections. Listing [[lst:configure-oc-cite+citar]] configures =org-cite=, =citar=,
|
||
and =org=.
|
||
|
||
#+caption[Configure =org-cite= with =citar=]:
|
||
#+caption: Configure =oc-cite= with =citar=.
|
||
#+name: lst:configure-oc-cite+citar
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'oc
|
||
(require 'oc-biblatex)
|
||
(require 'oc-csl)
|
||
|
||
(custom-set-variables
|
||
'(org-cite-export-processors '((latex biblatex)
|
||
(t csl)))
|
||
'(org-cite-global-bibliography '("~/VCS/research/refs.bib")))
|
||
|
||
(when (require 'citar nil 'noerror)
|
||
(custom-set-variables
|
||
'(org-cite-activate-processor 'citar)
|
||
'(org-cite-follow-processor 'citar)
|
||
'(org-cite-insert-processor 'citar))))
|
||
|
||
(with-eval-after-load 'org
|
||
(when (require 'citar nil 'noerror)
|
||
(custom-set-variables
|
||
'(citar-bibliography '("~/VCS/research/refs.bib"))
|
||
'(citar-file-extensions '("djvu" "pdf"))
|
||
'(citar-library-paths '("~/VCS/research/papers/"))))
|
||
|
||
(define-key org-mode-map (kbd "C-c b") #'org-cite-insert))
|
||
#+end_src
|
||
|
||
*** TODO Compare bibtex and biblatex
|
||
1. [[https://www.economics.utoronto.ca/osborne/latex/BIBTEX.HTM][Using bibtex: a short guide]]
|
||
2. [[https://www.tug.org/texlive//devsrc/Master/texmf-dist/doc/latex/biblatex/biblatex.pdf][The biblatex package]]
|
||
3. [[https://github.com/yuchenlin/rebiber][Rebiber: A tool for normalizing bibtex with official info]]
|
||
4. [[https://github.com/josephwright/biblatex-phys][A biblatex implementation of the AIP and APS bibliography style]]
|
||
|
||
*** [[info:org#Adding Hyperlink Types][Making Org hyperlink types (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:making-org-hyperlink-types
|
||
:END:
|
||
|
||
The code in listing [[lst:define-org-link-types]] defines =org-link= types for
|
||
backwards compatibility with [[https://github.com/jkitchin/org-ref][org-ref]].
|
||
|
||
#+caption[Define =org-link= types]:
|
||
#+caption: Define =org-link= types.
|
||
#+name: lst:define-org-link-types
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ol
|
||
(org-link-set-parameters
|
||
"ac*" :export (lambda (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\gls*{%s}" path))
|
||
(_ path))))
|
||
(org-link-set-parameters
|
||
"cite" :export (lambda (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\cite{%s}" path))
|
||
(_ path))))
|
||
(org-link-set-parameters
|
||
"eqref" :export (lambda (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\eqref{%s}" path))
|
||
(_ path))))
|
||
(org-link-set-parameters
|
||
"hyperlink" :export (lambda (path desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\hyperlink{%s}{%s}" path desc))
|
||
(_ path))))
|
||
(org-link-set-parameters
|
||
"label" :export (lambda (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\label{%s}" path))
|
||
(_ path))))
|
||
(org-link-set-parameters
|
||
"ref" :export (lambda (path _desc backend _info)
|
||
(pcase backend
|
||
(`latex (format "\\ref{%s}" path))
|
||
(_ path)))))
|
||
#+end_src
|
||
|
||
Listing [[lst:emacs-lisp-setup-patch-ol-info]] patches the function =org-info-export=
|
||
and the constant ~org-info-other-documents~, to export the [[info:org#External Links][info: links (info)]] in
|
||
this document to =html= and LaTeX correctly.
|
||
|
||
#+caption[Patch =ol-info=]:
|
||
#+caption: Patch =ol-info=.
|
||
#+name: lst:emacs-lisp-setup-patch-ol-info
|
||
#+begin_src emacs-lisp :exports code :silent
|
||
(with-eval-after-load 'ol-info
|
||
(defun org-info-export (path desc format)
|
||
"Export an info link.
|
||
See `org-link-parameters' for details about PATH, DESC and FORMAT."
|
||
(let* ((parts (split-string path "#\\|::"))
|
||
(manual (car parts))
|
||
(node (or (nth 1 parts) "Top")))
|
||
(pcase format
|
||
(`html
|
||
(format "<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))))
|
||
|
||
(make-variable-buffer-local 'org-info-emacs-documents)
|
||
(setq org-info-emacs-documents (delete "org" org-info-emacs-documents))
|
||
(make-variable-buffer-local 'org-info-other-documents)
|
||
(setq org-info-other-documents
|
||
(cl-union '(("consult" . "https://github.com/minad/consult")
|
||
("embark" . "https://github.com/oantolin/embark")
|
||
("marginalia" . "https://github.com/minad/marginalia")
|
||
("org" . "https://orgmode.org/org.html")
|
||
("orderless" . "https://github.com/oantolin/orderless")
|
||
("sly" . "https://joaotavora.github.io/sly/")
|
||
("vertico" . "https://github.com/minad/vertico"))
|
||
org-info-other-documents
|
||
:test #'equal)))
|
||
#+end_src
|
||
|
||
*** [[https://tecosaur.github.io/emacs-config/#translate-capital-keywords][Translate capital keywords (old) to lower case (new)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:convert-upper-to-lower-case-keywords
|
||
:END:
|
||
|
||
#+caption[Convert upper to lower case keywords]:
|
||
#+caption: Convert upper to lower case keywords.
|
||
#+name: lst:convert-upper-to-lower-case-keywords
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'org
|
||
(defun org-syntax-convert-keyword-case-to-lower ()
|
||
"Convert all #+KEYWORDS to #+keywords."
|
||
(interactive)
|
||
(save-excursion
|
||
(goto-char (point-min))
|
||
(let ((count 0)
|
||
(case-fold-search nil))
|
||
(while (re-search-forward "^[ \t]*#\\+[A-Z_]+" nil t)
|
||
(unless (s-matches-p "RESULTS" (match-string 0))
|
||
(replace-match (downcase (match-string 0)) t)
|
||
(setq count (1+ count))))
|
||
(message "Replaced %d keywords" count)))))
|
||
#+end_src
|
||
|
||
*** [[https://lists.gnu.org/archive/html/emacs-orgmode/2016-07/msg00394.html][Evaluate specific source blocks at load-time]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:load-time-specific-source-block-evaluation
|
||
:END:
|
||
|
||
[[https://emacs.stackexchange.com/questions/12938/how-can-i-evaluate-elisp-in-an-orgmode-file-when-it-is-opened][How to do load time source block evaluation]]
|
||
|
||
#+caption[Evaluate specific source blocks at load-time]:
|
||
#+caption: Evaluate specific source blocks at load-time.
|
||
#+name: lst:load-time-specific-source-block-evaluation
|
||
#+begin_src emacs-lisp
|
||
(defun my-org-eval-blocks-named (infix)
|
||
"Evaluate all source blocks having INFIX in their name."
|
||
(when (eq major-mode 'org-mode)
|
||
(let ((blocks
|
||
(org-element-map
|
||
(org-element-parse-buffer 'greater-element nil) 'src-block
|
||
(lambda (block)
|
||
(let ((name (org-element-property :name block)))
|
||
(when (and name (string-match-p infix name))
|
||
block))))))
|
||
(dolist (block blocks)
|
||
(goto-char (org-element-property :begin block))
|
||
(org-babel-execute-src-block)))))
|
||
|
||
;; Emacs looks for "Local variables:" after the last "?\n?\f".
|
||
(add-to-list 'safe-local-eval-forms
|
||
'(apply 'my-org-eval-blocks-named '("emacs-lisp-setup")))
|
||
(add-to-list 'safe-local-eval-forms
|
||
'(apply 'my-org-eval-blocks-named '("python-setup")))
|
||
#+end_src
|
||
|
||
*** [[info:org#LaTeX header and sectioning][Easy LaTeX preamble editing]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:easy-latex-preamble-editing
|
||
:END:
|
||
|
||
There are at least two ways (new and old) to edit the LateX preamble
|
||
=latex_header= and =latex-extra_header= export options easily in LaTeX source or
|
||
export blocks. This [[info:org#Top][Org (info)]] file uses the new way, but keeps the old way for
|
||
backwards compatibility.
|
||
|
||
The new way -- exploiting an idea of [[https://www.matem.unam.mx/~omar/][Omar Antolin Camarena]] -- is to code new
|
||
[[info:org#Editing Source Code][<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:emacs-lisp-setup-latex-header]] implements this way by means of two
|
||
new [[info:org#Editing Source Code][<LANGUAGE>-modes]]: =latex-header= and =latex-extra-header=.
|
||
|
||
#+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:emacs-lisp-setup-latex-header
|
||
#+begin_src emacs-lisp :exports code :silent
|
||
(with-eval-after-load 'org-src
|
||
(defun prefix-all-lines (prefix body)
|
||
(with-temp-buffer
|
||
(insert body)
|
||
(string-insert-rectangle (point-min) (point-max) prefix)
|
||
(buffer-string)))
|
||
|
||
(add-to-list 'org-src-lang-modes '("latex-header" . latex))
|
||
|
||
(defvar org-babel-default-header-args:latex-header
|
||
'((:exports . "results") (:results . "raw")))
|
||
|
||
(defun org-babel-execute:latex-header (body _params)
|
||
"Execute a block of LaTeX preamble lines with org-babel.
|
||
This function is called by `org-babel-execute-src-block' and
|
||
prefixes all lines with \"#+latex_header: \"."
|
||
(prefix-all-lines "#+latex_header: " body))
|
||
|
||
(add-to-list 'org-src-lang-modes '("latex-extra-header" . latex))
|
||
|
||
(defvar org-babel-default-header-args:latex-extra-header
|
||
'((:exports . "results") (:results . "raw")))
|
||
|
||
(defun org-babel-execute:latex-extra-header (body _params)
|
||
"Execute a block of LaTeX preamble lines with org-babel.
|
||
This function is called by `org-babel-execute-src-block' and
|
||
prefixes all lines with \"#+latex_extra_header: \"."
|
||
(prefix-all-lines "#+latex_extra_header: " body)))
|
||
#+end_src
|
||
|
||
The old way is to use a special export attribute as in the function
|
||
=org-latex-header-blocks-filter= in [[https://git.sr.ht/~bzg/org-contrib/tree/master/item/lisp/ox-extra.el][ox-extra.el]]. Apparently, nobody is using
|
||
this broken function (broken, since it relies on support only in org-mode before
|
||
=2014-11-11=). Listing [[lst:org-latex-header-blocks-filter]] proposes a fix for
|
||
=org-latex-header-blocks-filter=.
|
||
|
||
#+caption[Convert marked LaTeX export blocks to LaTeX header lines]:
|
||
#+caption: Convert marked LaTeX export blocks to LaTeX header lines.
|
||
#+name: lst:org-latex-header-blocks-filter
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ox
|
||
(defun org-latex-header-blocks-filter (backend)
|
||
"Convert marked LaTeX export blocks to \"#+latex_header: \" lines.
|
||
The marker is a line \"#+header: :header yes\" preceding the block.
|
||
|
||
For instance, the LaTeX export block
|
||
|
||
,#+header: :header yes
|
||
,#+begin_export latex
|
||
% This line converts to a LaTeX header line.
|
||
,#+end_export
|
||
|
||
converts to
|
||
|
||
\"#+latex_header: % This line converts to a LaTeX header line.\"."
|
||
(when (org-export-derived-backend-p backend 'latex)
|
||
(let ((blocks
|
||
(org-element-map
|
||
(org-element-parse-buffer 'greater-element nil) 'export-block
|
||
(lambda (block)
|
||
(let ((type (org-element-property :type block))
|
||
(header (org-export-read-attribute :header block :header)))
|
||
(when (and (string= type "LATEX") (string= header "yes"))
|
||
block))))))
|
||
(mapc (lambda (block)
|
||
;; Set point to where to insert LaTeX header lines
|
||
;; after deleting the block.
|
||
(goto-char (org-element-property :post-affiliated block))
|
||
(let ((lines
|
||
(split-string (org-element-property :value block) "\n")))
|
||
(delete-region (org-element-property :begin block)
|
||
(org-element-property :end block))
|
||
(dolist (line lines)
|
||
(insert (concat "#+latex_header: "
|
||
(replace-regexp-in-string "\\` *" "" line)
|
||
"\n")))))
|
||
;; Reverse to go upwards to avoid wrecking the list of
|
||
;; block positions in the file that would occur in case
|
||
;; of going downwards.
|
||
(reverse blocks))))))
|
||
#+end_src
|
||
|
||
This file uses the new way, while keeping the old way for backwards
|
||
compatibility, because the new way feels less hackish than the old way. A
|
||
practical difference is that new way source blocks (contrary to old way export
|
||
blocks) do not work in [[info:org#Export Settings][#+SETUPFILE: <FILE>]], but only in [[info:org#Export Settings][#+INCLUDE: <FILE>]] files.
|
||
|
||
*** [[info:org#Export Settings][#+SETUPFILE: and #+INCLUDE: usage]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:setupfile+include-usage
|
||
:END:
|
||
|
||
Evaluation of the source block in listing
|
||
[[lst:make-source-block-with-export-keyword-settings]] produces the source block
|
||
that exports to listing [[lst:source-file-export-keyword-settings]] which shows the
|
||
first six lines of this [[file:README.org]] file. The last two lines show that
|
||
[[file:setup-include.org][setup-include.org]] is the argument for [[info:org#Export Settings][#+SETUPFILE:]] *and* [[info:org#Export Settings][#+INCLUDE:]].
|
||
|
||
#+caption[Make setup and include file export keyword settings source block]:
|
||
#+caption: The shell script to make the source block containing the export
|
||
#+caption: keyword settings.
|
||
#+name: lst:make-source-block-with-export-keyword-settings
|
||
#+begin_src shell :exports both :results drawer
|
||
echo "#+caption[Source file export keyword settings]:"
|
||
echo "#+caption: The first six lines of README.org containing the export"
|
||
echo "#+caption: keyword settings."
|
||
echo "#+name: lst:source-file-export-keyword-settings"
|
||
echo "#+begin_src org :tangle no"
|
||
head -n 6 README.org
|
||
echo -n "#+end_src"
|
||
#+end_src
|
||
|
||
#+RESULTS: lst:make-source-block-with-export-keyword-settings
|
||
:results:
|
||
#+caption[Source file export keyword settings]:
|
||
#+caption: The first six lines of README.org containing the export
|
||
#+caption: keyword settings.
|
||
#+name: lst:source-file-export-keyword-settings
|
||
#+begin_src org :tangle no
|
||
#+title: Emacs setup for use with LaTeX, Org, and Python
|
||
#+author: Gerard Vermeulen
|
||
#+latex_class: article
|
||
#+latex_class_options: [11pt,a4paper,english,svgnames,tables]
|
||
#+setupfile: "setup-include.org"
|
||
#+include: "setup-include.org"
|
||
#+end_src
|
||
:end:
|
||
|
||
Listing [[lst:setup-include-export-keyword-settings]] tangles into the
|
||
[[file:setup-include.org][setup-include.org]] file and listing [[lst:by-backend-kbd-org-macro]] defines
|
||
the tools for the [[https://orgmode.org/][Org mode]] =kbd= macro in [[file:setup-include.org][setup-include.org]].
|
||
|
||
#+caption[Setup and include file export keyword settings]:
|
||
#+caption: Setup and include file export keyword settings.
|
||
#+name: lst:setup-include-export-keyword-settings
|
||
#+begin_src org
|
||
,#+babel: :cache no
|
||
,#+macro: kbd (eval (by-backend-kbd-org-macro $1))
|
||
,#+options: ^:{}
|
||
,#+property: header-args:emacs-lisp :exports code :results silent :tangle init.el
|
||
,#+property: header-args:org :tangle setup-include.org
|
||
,#+startup: content
|
||
#+end_src
|
||
|
||
#+caption[Define the tools of the org-mode =kbd= macro]:
|
||
#+caption: Define the tools for the org-mode =kbd= macro.
|
||
#+name: lst:by-backend-kbd-org-macro
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ox
|
||
(autoload 'htmlize-protect-string "htmlize" nil t)
|
||
|
||
(defmacro by-backend (&rest body)
|
||
`(cl-case org-export-current-backend ,@body))
|
||
|
||
(defun by-backend-kbd-org-macro (keys)
|
||
(by-backend
|
||
(html (format "@@html:<kbd>%s</kbd>@@" (htmlize-protect-string keys)))
|
||
(latex (format "@@latex:\\colorbox{PowderBlue}{\\texttt{%s}}@@" keys)))))
|
||
#+end_src
|
||
|
||
Listing [[lst:use-latex-header-1]], [[lst:use-latex-header-2]], [[lst:use-latex-header-3]],
|
||
and [[lst:use-latex-header-4]] tangle into the [[file:setup-include.org][setup-include.org]] file in order to
|
||
create the [[https://www.latex-project.org/][LaTeX]] preamble.
|
||
|
||
#+caption[LaTeX preamble: language, lists and floats]:
|
||
#+caption: LaTeX preamble: language, lists and floats.
|
||
#+name: lst:use-latex-header-1
|
||
#+begin_src org
|
||
,#+begin_src latex-header
|
||
|
||
% LANGUAGE:
|
||
\usepackage{babel}
|
||
\usepackage{fvextra}
|
||
\usepackage{csquotes}
|
||
|
||
% LISTS:
|
||
\usepackage{enumitem}
|
||
\setlist{noitemsep}
|
||
|
||
% LISTINGS:
|
||
% Section 2.6 of caption-eng.pdf (texdoc caption) explains that the sign
|
||
% of "skip" depends on the assumption "position=above" or "position=below".
|
||
% The assumption should match the real caption position in the LaTeX code.
|
||
\usepackage{caption}
|
||
\usepackage[newfloat]{minted}
|
||
\captionsetup[listing]{position=below,skip=0em}
|
||
\usemintedstyle{xcode}
|
||
|
||
% TABLES:
|
||
% https://tex.stackexchange.com/questions/341205/
|
||
% what-is-the-difference-between-tabular-tabular-and-tabularx-environments
|
||
% https://emacs.stackexchange.com/questions/26179/
|
||
% change-org-mode-table-style-just-for-latex-export
|
||
% https://tex.stackexchange.com/questions/468585/
|
||
% table-formatting-using-siunitx
|
||
\usepackage{booktabs}
|
||
\usepackage{colortbl}
|
||
\usepackage{tabularx} % DANGER: beware of Org table :width and :align options!
|
||
|
||
,#+end_src
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: page layout]:
|
||
#+caption: LaTeX preamble: page layout.
|
||
#+name: lst:use-latex-header-2
|
||
#+begin_src org
|
||
,#+begin_src latex-header
|
||
% PAGE LAYOUT:
|
||
\usepackage{fancyhdr}
|
||
\usepackage{lastpage}
|
||
\usepackage[
|
||
headheight=20mm,
|
||
top=40mm,
|
||
bottom=20mm,
|
||
left=60pt,
|
||
right=60pt,
|
||
heightrounded,
|
||
verbose,
|
||
]{geometry}
|
||
|
||
% TECHNICS:
|
||
\usepackage{siunitx}
|
||
\usepackage{tikz}
|
||
|
||
,#+end_src
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: float barriers]:
|
||
#+caption: LaTeX preamble: float barriers.
|
||
#+name: lst:use-latex-header-3
|
||
#+begin_src org
|
||
,#+begin_src latex-header
|
||
% FLOAT BARRIERS:
|
||
% https://tex.stackexchange.com/questions/118662/use-placeins-for-subsections
|
||
% Make section an implicit float barrier:
|
||
\usepackage[section]{placeins}
|
||
% Make subsection an implicit float barrier:
|
||
\makeatletter
|
||
\AtBeginDocument{%
|
||
\expandafter\renewcommand\expandafter\subsection\expandafter{%
|
||
\expandafter\@fb@secFB\subsection
|
||
}%
|
||
}
|
||
\makeatother
|
||
% Make subsubsection an implicit float barrier:
|
||
\makeatletter
|
||
\AtBeginDocument{%
|
||
\expandafter\renewcommand\expandafter\subsubsection\expandafter{%
|
||
\expandafter\@fb@secFB\subsubsection
|
||
}%
|
||
}
|
||
\makeatother
|
||
|
||
,#+end_src
|
||
#+end_src
|
||
|
||
#+caption[LaTeX preamble: fancy headers and footers]:
|
||
#+caption: LaTeX preamble: fancy headers and footers.
|
||
#+name: lst:use-latex-header-4
|
||
#+begin_src org
|
||
,#+begin_src latex-header
|
||
% FANCY HEADERS AND FOOTERS:
|
||
% Add fancy headers and footers to normal pages.
|
||
\pagestyle{fancy}
|
||
\fancyhf{}
|
||
\renewcommand{\footrulewidth}{0.4pt}
|
||
\fancyfoot[C]{\emph{
|
||
Emacs setup for use with \LaTeX{}, Org, and Python -- Gerard Vermeulen}}
|
||
\renewcommand{\headrulewidth}{0.4pt}
|
||
\fancyhead[L]{\includegraphics[height=1.8cm]{Org-mode-unicorn.png}}
|
||
\fancyhead[C]{
|
||
Page: \thepage/\pageref{LastPage} \\
|
||
\text{ } \\
|
||
\text{ } \\
|
||
DRAFT
|
||
}
|
||
\fancyhead[R]{\includegraphics[height=1.8cm]{Emacs-logo.png}}
|
||
|
||
% Add fancy header and footer to custom titlepage.
|
||
% https://tex.stackexchange.com/questions/506102/
|
||
% adding-header-and-footer-to-custom-titlepage
|
||
\fancypagestyle{titlepage}{%
|
||
\fancyhf{}
|
||
\renewcommand{\footrulewidth}{0.4pt}
|
||
\fancyfoot[C]{\emph{
|
||
Emacs setup for use with \LaTeX{}, Org, and Python -- Gerard Vermeulen}}
|
||
\renewcommand{\headrulewidth}{0.4pt}
|
||
\fancyhead[L]{\includegraphics[height=1.8cm]{Org-mode-unicorn.png}}
|
||
\fancyhead[C]{
|
||
\pageref{LastPage} pages \\
|
||
\text{ } \\
|
||
\text{ } \\
|
||
DRAFT
|
||
}
|
||
\fancyhead[R]{\includegraphics[height=1.8cm]{Emacs-logo.png}}
|
||
}
|
||
% #+latex_header: END.
|
||
,#+end_src
|
||
#+end_src
|
||
|
||
*** [[info:org#LaTeX specific export settings][Advanced LaTeX export settings]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:advanced-latex-export-settings
|
||
:END:
|
||
|
||
[[https://emacs.stackexchange.com/questions/47347/customizing-org-latex-title-command-to-edit-title-page][How to customize =org-latex-title-command= only in this buffer]]
|
||
|
||
#+caption[Define =my-ox-latex-export-buffer-local-variables=]:
|
||
#+caption: Define =my-ox-latex-export-buffer-local-variables=.
|
||
#+name: lst:emacs-lisp-setup-defun
|
||
#+begin_src emacs-lisp :results silent
|
||
(defun my-ox-latex-export-buffer-local-variables (title-page)
|
||
(with-eval-after-load 'ox
|
||
(make-variable-buffer-local 'org-export-before-parsing-hook)
|
||
(cl-pushnew #'org-latex-header-blocks-filter
|
||
org-export-before-parsing-hook))
|
||
(when (require 'ox-latex nil 'noerror)
|
||
(make-variable-buffer-local 'org-latex-classes)
|
||
(cl-pushnew '("article-local"
|
||
"\\documentclass[11pt]{article}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[PACKAGES]
|
||
[EXTRA]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
|
||
org-latex-classes :key #'car :test #'equal)
|
||
|
||
(make-variable-buffer-local 'org-latex-title-command)
|
||
(setq org-latex-title-command (concat title-page))
|
||
|
||
(make-variable-buffer-local 'org-latex-toc-command)
|
||
(setq org-latex-toc-command
|
||
(mapconcat 'identity '(""
|
||
"\\tableofcontents\\label{toc}"
|
||
"\\listoffigures"
|
||
"\\listoflistings"
|
||
"\\listoftables"
|
||
"\\newpage"
|
||
"") "\n"))
|
||
|
||
(make-variable-buffer-local 'org-latex-subtitle-format)
|
||
(setq org-latex-subtitle-format "")))
|
||
#+end_src
|
||
|
||
Listing [[lst:emacs-lisp-setup-call]] initializes the buffer local variables
|
||
=org-export-before-parsing-hook=, =org-latex-classes=,
|
||
=org-latex-title-command=, =org-latex-toc-command=, and
|
||
=org-latex-subtitle-format=.
|
||
|
||
#+caption[Call =my-ox-latex-export-local-variables=]:
|
||
#+caption: Use the setup macro for =ox-latex=.
|
||
#+name: lst:emacs-lisp-setup-call
|
||
#+header: :var title-page=lst/title-page
|
||
#+begin_src emacs-lisp :tangle no
|
||
(my-ox-latex-export-buffer-local-variables title-page)
|
||
#+end_src
|
||
|
||
#+caption[Show a title-page example for =org-latex-title-command=]:
|
||
#+caption: Show a title-page example for =org-latex-title-command=.
|
||
#+name: lst/title-page
|
||
#+begin_src latex :exports code :results silent :tangle no
|
||
\begin{titlepage}
|
||
%% https://tex.stackexchange.com/questions/506102/
|
||
%% adding-header-and-footer-to-custom-titlepage
|
||
\thispagestyle{titlepage}
|
||
\begin{center}
|
||
%% Title
|
||
\begin{Huge}
|
||
{\bf %t} \\
|
||
\vspace{1em}
|
||
\end{Huge}
|
||
%% Author
|
||
\begin{Large}
|
||
{\bf %a} \\
|
||
\vspace{1em}
|
||
\end{Large}
|
||
\end{center}
|
||
\end{titlepage}
|
||
#+end_src
|
||
|
||
*** [[https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-LaTeX.html#orgcc214c1][Worg: backend dependent execution]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:backend-dependent-execution-update
|
||
:END:
|
||
|
||
#+caption[Worg update: backend dependent header arguments]:
|
||
#+caption: Worg update: backend dependent header arguments.
|
||
#+name: lst:backend-dependent-execution-update
|
||
#+begin_src org :tangle worg-backend-dependent-execution-update.org
|
||
,#+latex_header: \usepackage{tikz}
|
||
|
||
,#+header: :file (by-backend (html "tree.svg") (latex nil))
|
||
,#+header: :results (by-backend (html "raw file") (latex "latex replace"))
|
||
,#+header: :headers '("\\usepackage{tikz}\n")
|
||
,#+begin_src latex
|
||
\usetikzlibrary{trees}
|
||
\begin{tikzpicture}
|
||
\node [circle, draw, fill=red!20] at (0,0) {1}
|
||
child { node [circle, draw, fill=blue!30] {2}
|
||
child { node [circle, draw, fill=green!30] {3} }
|
||
child { node [circle, draw, fill=yellow!30] {4} }};
|
||
\end{tikzpicture}
|
||
,#+end_src
|
||
|
||
# Setup
|
||
,#+begin_src emacs-lisp :exports none
|
||
(with-eval-after-load 'ox
|
||
(setq org-babel-latex-htlatex "htlatex")
|
||
(defmacro by-backend (&rest body)
|
||
`(cl-case org-export-current-backend ,@body)))
|
||
,#+end_src
|
||
#+end_src
|
||
|
||
This section updates the outdated [[https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-LaTeX.html#orgcc214c1][Worg: backend dependent execution]] example to
|
||
Emacs-27.2 and Org-9.5.2. It shows how to export [[https://en.wikipedia.org/wiki/PGF/TikZ][PGF/TikZ]] images to:
|
||
1. [[https://en.wikipedia.org/wiki/PDF][PDF]] by embedding in [[https://www.latex-project.org/][LaTeX]].
|
||
2. [[https://en.wikipedia.org/wiki/HTML][HTML]] by passing from [[https://www.latex-project.org/][LaTeX]] by [[https://en.wikipedia.org/wiki/PDF][PDF]] and to [[https://en.wikipedia.org/wiki/Scalable_Vector_Graphics][SVG]].
|
||
Listing [[lst:backend-dependent-execution-update]] tangles to
|
||
[[file:worg-backend-dependent-execution-update.org][worg-backend-dependent-execution-update.org]] as either a standalone example or an
|
||
example for inclusion. Finally, inclusion of
|
||
[[file:worg-backend-dependent-execution-update.org][worg-backend-dependent-execution-update.org]] produces here the figure with the
|
||
nodes 1, 2, 3, and 4:\\
|
||
#+include: worg-backend-dependent-execution-update.org
|
||
How to include such figures as floats remains an open question.
|
||
|
||
*** [[https://orgmode.org/worg/dev/org-syntax-edited.html][Org Syntax]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:org-syntax
|
||
:END:
|
||
|
||
Two tools to grok how [[https://orgmode.org/worg/dev/org-element-api.html][Org mode parsing]] works are the [[https://orgmode.org/worg/dev/org-syntax-edited.html][Org Syntax]] specification
|
||
and the [[http://xahlee.info/emacs/emacs/elisp_parse_org_mode.html][Org mode parser tutorial]]. The [[https://orgmode.org/worg/dev/org-element-api.html][Org element parsing API]] boils down to three
|
||
functions:
|
||
1. The function [[https://orgmode.org/worg/dev/org-element-api.html#global][~org-element-parse-buffer~]] implements a fully recursive buffer
|
||
parser that returns an abstract syntax tree.
|
||
2. The functions [[https://orgmode.org/worg/dev/org-element-api.html#local][~org-element-at-point~ and ~org-element-context~]] return
|
||
information on the document structure around point either at the element
|
||
level or at the object level in case of ~org-element-context~.
|
||
Listing [[lst:grok-org-element-tree]] improves the [[http://xahlee.info/emacs/emacs/elisp_parse_org_mode.html][Org mode parser tutorial]] by
|
||
defining interactive wrappers that pretty print the results of those
|
||
non-interactive =org-element= functions to an =Emacs-lisp= buffer.
|
||
|
||
#+caption[Grok how =org-element= parses your document]:
|
||
#+caption: Grok how =org-element= parses your document.
|
||
#+name: lst:grok-org-element-tree
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'org-element
|
||
(when (autoload 'pp-display-expression "pp")
|
||
(defconst grok-org-output
|
||
"*Grok Org Element Output*"
|
||
"Grok Org output buffer name.")
|
||
|
||
(defun grok-org-element-at-point ()
|
||
(interactive)
|
||
(pp-display-expression
|
||
(org-element-at-point) grok-org-output))
|
||
|
||
(defun grok-org-element-context ()
|
||
(interactive)
|
||
(pp-display-expression
|
||
(org-element-context) grok-org-output))
|
||
|
||
(defun grok-org-element-parse-buffer ()
|
||
(interactive)
|
||
(let ((what (completing-read
|
||
"granularity: "
|
||
'(headline element greater-element object)
|
||
nil 'require-match)))
|
||
(pp-display-expression
|
||
(org-element-parse-buffer what) grok-org-output)))
|
||
|
||
(defun grok-org-heading-components ()
|
||
(interactive)
|
||
(pp-display-expression
|
||
(org-heading-components) grok-org-output))))
|
||
#+end_src
|
||
|
||
* Programming
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:programming
|
||
:END:
|
||
|
||
** Common Lisp programming
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:common-lisp-programming
|
||
:END:
|
||
Listing [[lst:configure-sly]] configures the [[info:sly#Top][Sly (info)]] Common Lisp IDE for Emacs
|
||
for use with [[http://www.sbcl.org/][Steel Bank Common Lisp (sbcl)]]:
|
||
1. It configures =sly-default-lisp= and =sly-lisp-implementations= as in the ~sly~ documentation string instead of in the [[info:sly#Multiple Lisps][multiple lisps (info)]] manual.
|
||
2. It ensures the [[info:sly#Auto-SLY][automatic connection to the lisp server (info)]] when opening a
|
||
Common Lisp file.
|
||
3. It configures searching documentation in the [[http://www.lispworks.com/documentation/HyperSpec/Front/][Common Lisp HyperSpec]] according
|
||
to the [[info:sly#Basic customization][basic customization (info)]] manual.
|
||
Finally, listing [[lst:configure-sly]] uses a technique to [[info:sly#Loading Slynk faster][load Slynk faster (info)]]
|
||
by means of a custom core file in src_emacs-lisp{no-littering-var-directory}.
|
||
Listing [[lst:sbcl-core-for-sly]] tangles to a script to dump such a [[http://www.sbcl.org/][SBCL]] core.
|
||
|
||
#+caption[Configure =sly=]:
|
||
#+caption: Configure =sly=.
|
||
#+name: lst:configure-sly
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'sly
|
||
(custom-set-variables
|
||
;; Customize `sly-default-lisp' instead of `inferior-lisp-program',
|
||
;; because `sly' uses `inferior-lisp-program' only as a backwards
|
||
;; compatibility fallback option.
|
||
'(sly-default-lisp 'sbcl)
|
||
`(sly-lisp-implementations
|
||
'((sbcl (,(executable-find "sbcl")
|
||
"--core"
|
||
,(no-littering-expand-var-file-name "sbcl.core-for-sly"))))))
|
||
|
||
(add-hook 'sly-mode-hook
|
||
(defun on-sly-mode-hook ()
|
||
(unless (sly-connected-p)
|
||
(save-excursion (sly)))))
|
||
|
||
(cond
|
||
((eq system-type 'darwin)
|
||
(setq common-lisp-hyperspec-root
|
||
"file:///usr/local/share/doc/hyperspec/HyperSpec/")
|
||
(setq common-lisp-hyperspec-symbol-table
|
||
(concat common-lisp-hyperspec-root "Data/Map_Sym.txt")))
|
||
((eq system-type 'gnu/linux)
|
||
(setq common-lisp-hyperspec-root
|
||
"file:///usr/share/doc/hyperspec-7.0/HyperSpec/"))
|
||
(t (message "Default Common Lisp HyperSpec access")))
|
||
|
||
(define-key sly-prefix-map (kbd "M-h") #'sly-documentation-lookup))
|
||
#+end_src
|
||
|
||
#+caption[Script to dump a SBCL core for the Sly Common Lisp IDE]:
|
||
#+caption: Script to dump a SBCL core for the Sly Common Lisp IDE.
|
||
#+header: :tangle-mode (identity #o755)
|
||
#+name: lst:sbcl-core-for-sly
|
||
#+begin_src shell :noeval :tangle ~/bin/sbcl.core-for-sly
|
||
#!/bin/sh
|
||
|
||
sbcl <<EOF
|
||
(mapc 'require '(sb-bsd-sockets sb-posix sb-introspect sb-cltl2 asdf))
|
||
(save-lisp-and-die "sbcl.core-for-sly")
|
||
EOF
|
||
|
||
# Local Variables:
|
||
# mode: shell-script
|
||
# sh-indentation: 2
|
||
# sh-basic-offset: 2
|
||
# End:
|
||
#+end_src
|
||
|
||
*** [[https://courses.cs.northwestern.edu/325/][CS 325 AI Programming]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:quicklisp
|
||
: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]] 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.
|
||
|
||
#+caption[Download and verify =quicklisp=]:
|
||
#+caption: Download and verify =quicklisp=.
|
||
#+name: lst:download+verify-quicklisp
|
||
#+begin_src shell :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 :noeval :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[A =quicklisp= sbclrc file]:
|
||
#+caption: A =quicklisp= sbclrc file.
|
||
#+name: lst:quicklisp-sbclrc-file
|
||
#+begin_src lisp :tangle ~/.sbclrc
|
||
;;; Hey Emacs, this is my -*- lisp -*- .sbclrc file.
|
||
#-quicklisp
|
||
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
||
(user-homedir-pathname))))
|
||
(when (probe-file quicklisp-init)
|
||
(load quicklisp-init)))
|
||
#+end_src
|
||
|
||
** [[info:eintr#Top][Emacs Lisp Programming (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-lisp-programming
|
||
:END:
|
||
|
||
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]]
|
||
|
||
** [[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 two packages:
|
||
1. [[https://github.com/joaotavora/eglot][Eglot - Emacs polyGLOT: an Emacs LSP client that stays out of your way]]. The
|
||
maintainer also contributes to Emacs itself and has a deep understanding of
|
||
[[https://sheer.tj/the_way_of_emacs.html][the Way of Emacs]]. He refuses to add new features without seeing how they fit
|
||
into [[https://sheer.tj/the_way_of_emacs.html][the Way of Emacs]] as this discussion on [[https://github.com/joaotavora/eglot/issues/523][org-mode source code blocks]]
|
||
shows.
|
||
2. [[https://github.com/pythonic-emacs/anaconda-mode][Anaconda - code navigation, documentation lookup, and completion for Python]].
|
||
In my opinion, [[https://github.com/joaotavora/eglot][eglot]] has more potential than [[https://github.com/pythonic-emacs/anaconda-mode][anaconda]], but [[https://github.com/pythonic-emacs/anaconda-mode][anaconda]] is
|
||
compatible with [[info:org#Editing Source Code][source code block editing]] while [[https://github.com/joaotavora/eglot][eglot]] is not.
|
||
|
||
Here are a few links covering how to integrate Emacs, Python and a Python LSP
|
||
server, before plunging into the configuring 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]]
|
||
|
||
*** [[https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/progmodes/python.el][Python-mode]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:python-mode
|
||
:END:
|
||
Listing [[lst:configure-python]] tells the Python shell to disregard its environment
|
||
variables (in particular =PYTHONSTARTUPFILE=).
|
||
|
||
#+caption[Configure =python=]:
|
||
#+caption: Configure =python=.
|
||
#+name: lst:configure-python
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'python
|
||
(custom-set-variables
|
||
'(python-indent-guess-indent-offset nil)
|
||
'(python-shell-interpreter-args "-i -E")))
|
||
#+end_src
|
||
|
||
*** [[https://github.com/pyenv/pyenv][Pyenv]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:pyenv
|
||
:END:
|
||
Listing [[lst:enable-pyenv-mode]] configures and enables =pyenv-mode=.
|
||
|
||
#+caption[Enable =pyenv-mode=]:
|
||
#+caption: Enable =pyenv-mode=.
|
||
#+name: lst:enable-pyenv-mode
|
||
#+begin_src emacs-lisp
|
||
(when (and (executable-find "pyenv")
|
||
(require 'pyenv-mode nil 'noerror))
|
||
(pyenv-mode +1)
|
||
(pyenv-mode-set "3.9.10/envs/python-3.9.10")
|
||
;; Stop shadowing the org-mode-map "C-c C-s" binding.
|
||
(define-key pyenv-mode-map (kbd "C-c C-s") nil))
|
||
#+end_src
|
||
|
||
*** [[https://github.com/joaotavora/eglot][Eglot]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:eglot
|
||
:END:
|
||
Listing [[lst:configure-eglot+python-lsp-server-for-python]] tangles to
|
||
=user-init-file= and 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]].
|
||
In order to enable all builtin [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] capabilities, ensure
|
||
installation of 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]], [[https://github.com/python-rope/rope#readme][rope]],
|
||
and [[https://github.com/google/yapf#readme][yapf]]. In addition, install the [[https://github.com/emanspeaks/pyls-flake8#readme][pyls-flake8]] plugin to let [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]]
|
||
use [[https://github.com/PyCQA/flake8][flake8]].
|
||
|
||
Listing [[lst:on-hack-local-variables-hook-eglot-maybe]] defines a hook function to
|
||
launch [[https://github.com/joaotavora/eglot][eglot]] in presence of a proper [[info:emacs#Directory Variables][.dir-locals.el]] file in the root directory
|
||
of any [[https://www.python.org][Python]] project. Listing [[lst:eglot-directory-variables-for-python]] shows
|
||
such a proper [[info:emacs#Directory Variables][.dir-locals.el]] file.
|
||
|
||
#+caption[Configure =eglot= with =python-lsp-server= for Python]:
|
||
#+caption: Configure =eglot= with =python-lsp-server= for Python.
|
||
#+name: lst:configure-eglot+python-lsp-server-for-python
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'eglot
|
||
;; (setq eglot-server-programs '((python-mode "pylsp")))
|
||
(add-to-list 'eglot-server-programs '(python-mode "pylsp"))
|
||
|
||
(setq-default
|
||
eglot-workspace-configuration
|
||
'(;; Disable the `:pyls_flake8' plugin to fall back to pycodestyle.
|
||
(:pylsp . (:plugins (:pyls_flake8 (:enabled t))))
|
||
(:pylsp . (:plugins (:jedi_completion (:cache_for ["astropy"]))))
|
||
(:pylsp . (:plugins (:jedi (:auto_import_modules ["numpy"])))))))
|
||
#+end_src
|
||
|
||
#+caption[Start =eglot= in case of a proper =dir-local-variables-alist=]:
|
||
#+caption: Start =eglot= in case of a proper =dir-local-variables-alist=.
|
||
#+name: lst:on-hack-local-variables-hook-eglot-maybe
|
||
#+begin_src emacs-lisp
|
||
(when (fboundp 'eglot-ensure)
|
||
;; The two hooks `after-change-major-mode-hook' and
|
||
;; `hack-local-variables-hook' are OK, but language mode hooks like
|
||
;; `python-mode-hook' are not.
|
||
(add-hook 'hack-local-variables-hook
|
||
(defun on-hack-local-variables-hook-eglot-maybe ()
|
||
(when (and (derived-mode-p 'python-mode)
|
||
(assoc 'eglot-workspace-configuration
|
||
dir-local-variables-alist))
|
||
(eglot-ensure)))))
|
||
#+end_src
|
||
|
||
#+caption[Propose =directory-variables= to launch =eglot=]:
|
||
#+caption: Propose =directory-variables= in the root of any Python project to
|
||
#+caption: launch =eglot=.
|
||
#+name: lst:eglot-directory-variables-for-python
|
||
#+begin_src emacs-lisp :tangle dir-locals.el
|
||
;; Proposal for a .dir-locals.el file in the root of any Python project.
|
||
((python-mode
|
||
. ((eglot-workspace-configuration
|
||
. (;; Disable the `:pyls_flake8' plugin to fall back to pycodestyle.
|
||
(:pylsp . (:plugins (:pyls_flake8 (:enabled t))))
|
||
(:pylsp . (:plugins (:jedi (:auto_import_modules ["numpy"]))))
|
||
(:pylsp . (:plugins (:jedi_completion (:cache_for ["astropy"])))))))))
|
||
#+end_src
|
||
|
||
Allthough the configuration of [[https://github.com/pythonic-emacs/blacken#readme][blacken]] is not explicit, I use it to format all
|
||
new Python code with the [[https://black.readthedocs.io/en/stable/index.html][Python Black code formatter]]. Listing
|
||
[[lst:pyproject-toml-kick-off]] and [[lst:setup-cfg-kick-off]] implement the rules in
|
||
[[https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html][using black with other tools]] in order to make [[https://flake8.pycqa.org/en/latest/][flake8]] or [[https://pycodestyle.pycqa.org/en/latest/][pycodestyle]] agree with
|
||
[[https://black.readthedocs.io/en/stable/index.html][black's uncompromising style]].
|
||
|
||
#+caption[Kick starting a =pyproject.toml= file]:
|
||
#+caption: Kick starting a =pyproject.toml= file.
|
||
#+name: lst:pyproject-toml-kick-off
|
||
#+begin_src toml :tangle pyproject.toml
|
||
[build-system]
|
||
requires = ["setuptools", "wheel"]
|
||
build-backend = "setuptools.build_meta"
|
||
|
||
[tool.black]
|
||
line-length = 88
|
||
#+end_src
|
||
|
||
#+caption[Kick starting a =setup.cfg= file]:
|
||
#+caption: Kick starting a =setup.cfg= file.
|
||
#+name: lst:setup-cfg-kick-off
|
||
#+begin_src toml :tangle setup.cfg
|
||
[flake8]
|
||
max-line-length = 88
|
||
extend-ignore = E203
|
||
|
||
[pycodestyle]
|
||
ignore = E203
|
||
max-line-length = 88
|
||
#+end_src
|
||
|
||
[[https://jedi.readthedocs.io/en/latest/][Jedi]] provides grammar checking and completion candidates to [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]].
|
||
Only [[https://jedi.readthedocs.io/en/latest/docs/changelog.html][jedi-0.18.1]] works with for instance [[https://numpy.org/][numpy-1.22.0]] in the sense that it does
|
||
not choke on universal functions provided that [[https://jedi.readthedocs.io/en/latest/][jedi]] does not parse but imports
|
||
[[https://numpy.org/][numpy-1.22.0]] (see [[https://github.com/davidhalter/jedi/issues/1744][jedi issue #1744]], [[https://github.com/davidhalter/jedi/issues/1745][#1745]], and [[https://github.com/davidhalter/jedi/issues/1746][#1746]]). Since the universal
|
||
functions are neither builtin methods nor data instances but a kind of "callable
|
||
instances", the [[https://docs.python.org/3/library/inspect.html][Python inspect]] module also fails to handle the universal
|
||
functions properly.
|
||
|
||
Listing [[lst:make-pylsp-server-patch]] generates listing
|
||
[[lst:show-pylsp-server-patch]] that shows the patch to make [[https://jedi.readthedocs.io/en/latest/][jedi]] import
|
||
[[https://numpy.org/][numpy-1.22.0]] in order to serve the information to [[https://github.com/python-lsp/python-lsp-server][python-lsp-server]] allowing it
|
||
to handle universal functions. Listing [[lst:show-pylsp-server-patch]] tangles to
|
||
=pylsp-auto-import-modules.patch=.
|
||
|
||
#+caption[Make =pylsp-auto-import-modules.patch= listing]:
|
||
#+caption: Make =pylsp-auto-import-modules.patch= listing.
|
||
#+name: lst:make-pylsp-server-patch
|
||
#+begin_src shell :exports both :results drawer :shebang
|
||
echo "#+attr_latex: :options breaklines"
|
||
echo "#+caption[Show and tangle =pylsp-auto-import-modules.patch=]:"
|
||
echo "#+caption: Show and tangle =pylsp-auto-import-modules.patch=."
|
||
echo "#+name: lst:show-pylsp-server-patch"
|
||
echo "#+begin_src diff :exports code :tangle pylsp-auto-import-modules.patch"
|
||
git -C $HOME/VCS/python-lsp-server diff
|
||
echo "#+end_src"
|
||
#+end_src
|
||
|
||
#+RESULTS: lst:make-pylsp-server-patch
|
||
:results:
|
||
#+attr_latex: :options breaklines
|
||
#+caption[Show and tangle =pylsp-auto-import-modules.patch=]:
|
||
#+caption: Show and tangle =pylsp-auto-import-modules.patch=.
|
||
#+name: lst:show-pylsp-server-patch
|
||
#+begin_src diff :exports code :tangle pylsp-auto-import-modules.patch
|
||
diff --git a/pylsp/config/schema.json b/pylsp/config/schema.json
|
||
index c29d78b..4f30101 100644
|
||
--- a/pylsp/config/schema.json
|
||
+++ b/pylsp/config/schema.json
|
||
@@ -69,6 +69,14 @@
|
||
"default": null,
|
||
"description": "List of errors and warnings to enable."
|
||
},
|
||
+ "pylsp.plugins.jedi.auto_import_modules": {
|
||
+ "type": "array",
|
||
+ "default": ["numpy", "gi"],
|
||
+ "items": {
|
||
+ "type": "string"
|
||
+ },
|
||
+ "description": "List of module names for jedi to import (jedi.settings.auto_import_modules)."
|
||
+ },
|
||
"pylsp.plugins.jedi.extra_paths": {
|
||
"type": "array",
|
||
"default": [],
|
||
diff --git a/pylsp/workspace.py b/pylsp/workspace.py
|
||
index bf312f6..4758b53 100644
|
||
--- a/pylsp/workspace.py
|
||
+++ b/pylsp/workspace.py
|
||
@@ -14,6 +14,8 @@ from . import lsp, uris, _utils
|
||
|
||
log = logging.getLogger(__name__)
|
||
|
||
+DEFAULT_AUTO_IMPORT_MODULES = ["numpy", "gi"]
|
||
+
|
||
# TODO: this is not the best e.g. we capture numbers
|
||
RE_START_WORD = re.compile('[A-Za-z_0-9]*$')
|
||
RE_END_WORD = re.compile('^[A-Za-z_0-9]*')
|
||
@@ -252,6 +254,8 @@ class Document:
|
||
|
||
if self._config:
|
||
jedi_settings = self._config.plugin_settings('jedi', document_path=self.path)
|
||
+ jedi.settings.auto_import_modules = jedi_settings.get('auto_import_modules',
|
||
+ DEFAULT_AUTO_IMPORT_MODULES)
|
||
environment_path = jedi_settings.get('environment')
|
||
extra_paths = jedi_settings.get('extra_paths') or []
|
||
env_vars = jedi_settings.get('env_vars')
|
||
#+end_src
|
||
:end:
|
||
|
||
*** [[https://github.com/pythonic-emacs/anaconda-mode][Anaconda]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:anaconda
|
||
:END:
|
||
Listing [[lst:configure-anaconda+company-for-python]] and
|
||
[[lst:define-my-toggle-anaconda-mode]] configure [[https://github.com/pythonic-emacs/anaconda-mode][anaconda]]. See [[https://github.com/jorgenschaefer/elpy/blob/8d0de310d41ebf06b22321a8534546447456870c/elpy.el#L2775][elpy-module-company]]
|
||
for how to handle ~company-backends~ as a local variable in listing
|
||
[[lst:configure-anaconda+company-for-python]]. The call to [[info:elisp#Advising Functions][advice-add]] in listing
|
||
[[lst:define-my-toggle-anaconda-mode]] opens Python =org-edit-src-code= buffers in
|
||
~anaconda-mode~.
|
||
|
||
#+caption[Configure =anaconda= with =company= for Python]:
|
||
#+caption: Configure =anaconda= with =company= for Python.
|
||
#+name: lst:configure-anaconda+company-for-python
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'python
|
||
(with-eval-after-load 'company
|
||
(when (and (fboundp 'anaconda-mode)
|
||
(fboundp 'company-anaconda))
|
||
(defun my-disable-anaconda-mode ()
|
||
(when (derived-mode-p 'python-mode)
|
||
(anaconda-mode -1)
|
||
(make-variable-buffer-local 'company-backends)
|
||
(setq company-backends
|
||
(delq 'company-anaconda
|
||
(mapcar #'identity company-backends)))
|
||
(anaconda-eldoc-mode -1)))
|
||
(defun my-enable-anaconda-mode ()
|
||
(when (derived-mode-p 'python-mode)
|
||
(anaconda-mode +1)
|
||
(make-variable-buffer-local 'company-backends)
|
||
(setq company-backends
|
||
(cons 'company-anaconda
|
||
(delq 'company-semantic
|
||
(delq 'company-capf
|
||
(mapcar #'identity company-backends)))))
|
||
(anaconda-eldoc-mode
|
||
(if (file-remote-p default-directory) -1 1)))))))
|
||
#+end_src
|
||
|
||
#+caption[Define =my-toggle-anaconda-mode= for Python]:
|
||
#+caption: Define =my-toggle-anaconda-mode= for Python.
|
||
#+name: lst:define-my-toggle-anaconda-mode
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'python
|
||
(unless (and (fboundp 'my-disable-anaconda-mode)
|
||
(fboundp 'my-enable-anaconda-mode))
|
||
(when (fboundp 'anaconda-mode)
|
||
(defun my-disable-anaconda-mode ()
|
||
(when (derived-mode-p 'python-mode)
|
||
(anaconda-mode -1)
|
||
(anaconda-eldoc-mode -1)))
|
||
(defun my-enable-anaconda-mode ()
|
||
(when (derived-mode-p 'python-mode)
|
||
(anaconda-mode +1)
|
||
(anaconda-eldoc-mode
|
||
(if (file-remote-p default-directory) -1 1))))))
|
||
|
||
(when (fboundp 'my-enable-anaconda-mode)
|
||
(advice-add 'org-edit-src-code :after #'my-enable-anaconda-mode))
|
||
(when (and (fboundp 'my-disable-anaconda-mode)
|
||
(fboundp 'my-enable-anaconda-mode))
|
||
(defun my-toggle-anaconda-mode ()
|
||
"Toggle anaconda-mode with bells and whistles."
|
||
(interactive)
|
||
(if (bound-and-true-p anaconda-mode)
|
||
(my-disable-anaconda-mode)
|
||
(my-enable-anaconda-mode)))))
|
||
#+end_src
|
||
|
||
*** [[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 and either [[https://github.com/pythonic-emacs/anaconda-mode][anaconda]] or [[https://github.com/joaotavora/eglot][eglot]] works when coding certain functions of for
|
||
instance [[https://numpy.org/][numpy]] and [[https://scipy.org/][scipy]].
|
||
|
||
#+caption[Tangle the =example.py= file]:
|
||
#+caption: Tangle the =example.py= file.
|
||
#+name: lst:example-py
|
||
#+begin_src python :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
|
||
|
||
*** TODO Look into: editing facilities
|
||
1. [[https://github.com/douglasdavis/numpydoc.el/blob/main/numpydoc.el][Emacs extension to insert numpy style docstrings in function definitions]]
|
||
|
||
* [[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 several important packages in this
|
||
Emacs setup (for instance [[https://github.com/pythonic-emacs/anaconda-mode#readme][anaconda]], [[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
|
||
(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:s-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 several packages in this Emacs
|
||
setup ([[https://github.com/pythonic-emacs/anaconda-mode#readme][anaconda]], [[https://github.com/andras-simonyi/citeproc-el#readme][citeproc]], [[https://github.com/pythonic-emacs/pythonic#readme][pythonic]]) 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 several packages in this Emacs setup ([[https://github.com/pythonic-emacs/anaconda-mode#readme][anaconda]],
|
||
[[https://github.com/andras-simonyi/citeproc-el#readme][citeproc]], [[https://github.com/bdarcus/citar#readme][citar]], and [[https://github.com/pythonic-emacs/company-anaconda][company-anaconda]]) 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
|
||
(unless noninteractive
|
||
(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
|
||
(unless noninteractive
|
||
(when (require 'ws-butler nil 'noerror)
|
||
(custom-set-variables
|
||
'(ws-butler-keep-whitespace-before-point nil))
|
||
(add-hook 'prog-mode-hook #'ws-butler-mode)
|
||
(add-hook 'text-mode-hook #'ws-butler-mode)))
|
||
#+end_src
|
||
|
||
** [[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
|
||
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. [[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]].
|
||
2. [[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:configure-smartparens]] aims to configure [[https://github.com/Fuco1/smartparens][smartparens]] for Elisp,
|
||
LaTeX, Org, and Python.
|
||
|
||
#+caption[Configure =smartparens=]:
|
||
#+caption: Configure =smartparens=.
|
||
#+name: lst:configure-smartparens
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
;; To disables pairing of the quote character for lisp modes,
|
||
;; require smartparens-config instead of smartparens.
|
||
(when (require 'smartparens-config nil 'noerror)
|
||
(custom-set-variables
|
||
'(sp-base-key-bindings 'sp)
|
||
'(sp-override-key-bindings '(("C-(" . sp-backward-barf-sexp)
|
||
("C-)" . sp-forward-slurp-sexp))))
|
||
|
||
(dolist (hook '(prog-mode-hook text-mode-hook))
|
||
(add-hook hook #'smartparens-mode))
|
||
|
||
;; Hook on the specific `eval-expression-minibuffer-setup-hook'
|
||
;; and not on the general `minibuffer-setup-hook'.
|
||
(dolist (hook '(emacs-lisp-mode-hook
|
||
eval-expression-minibuffer-setup-hook
|
||
ielm-mode-hook
|
||
lisp-mode-hook
|
||
python-mode-hook
|
||
sly-mrepl-mode-hook))
|
||
(add-hook hook #'smartparens-strict-mode))
|
||
|
||
;; Tweak for the call to `smartparens-strict-mode' hooked on
|
||
;; `eval-expression-minibuffer-setup-hook'.
|
||
(sp-with-modes '(fundamental-mode ; first usage.
|
||
minibuffer-inactive-mode) ; later usage.
|
||
(sp-local-pair "'" nil :actions nil))
|
||
|
||
;; 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
|
||
|
||
** [[https://github.com/davidshepherd7/electric-operator#readme][Electric operators]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:electric-operators
|
||
:END:
|
||
|
||
Listing [[lst:configure-electric-operator-mode]] 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-mode=]:
|
||
#+caption: Configure =electric-operator-mode=.
|
||
#+name: lst:configure-electric-operator-mode
|
||
#+begin_src emacs-lisp
|
||
(when (fboundp 'electric-operator-mode)
|
||
(add-hook 'c-mode-common #'electric-operator-mode)
|
||
(add-hook 'python-mode-hook #'electric-operator-mode))
|
||
#+end_src
|
||
|
||
** [[https://joaotavora.github.io/yasnippet/][Smart snippets]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:smart-snippets
|
||
:END:
|
||
|
||
#+caption[Enable =yas-global-mode=]:
|
||
#+caption: Enable =yas-global-mode=.
|
||
#+name: lst:enable-yas-global-mode
|
||
#+begin_src emacs-lisp
|
||
(when (require 'yasnippet nil 'noerror)
|
||
(custom-set-variables
|
||
'(yas-alias-to-yas/prefix-p nil))
|
||
(yas-global-mode +1))
|
||
#+end_src
|
||
|
||
* [[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 tables by means of =org-narrow-to-table=.
|
||
|
||
#+caption[Configure =narrow-or-widen-dwim=]:
|
||
#+caption: Configure =narrow-or-widen-dwim=.
|
||
#+name: lst:configure-narrow-or-widen-dwim
|
||
#+begin_src emacs-lisp
|
||
(defun org-narrow-to-table ()
|
||
"Narrow buffer to current table."
|
||
(interactive)
|
||
(if (org-table-p)
|
||
(narrow-to-region (org-table-begin) (org-table-end))
|
||
(user-error "Not in a table")))
|
||
|
||
(defun narrow-or-widen-dwim (p)
|
||
"Widen if buffer is narrowed, narrow-dwim otherwise.
|
||
Dwim means: region, org-src-block, org-subtree, or defun,
|
||
whichever applies first. Narrowing to org-src-block actually
|
||
calls `org-edit-src-code'.
|
||
With prefix P, don't widen, just narrow even if buffer is
|
||
already narrowed."
|
||
(interactive "P")
|
||
(declare (interactive-only))
|
||
(cond ((and (buffer-narrowed-p) (not p)) (widen))
|
||
((and (bound-and-true-p org-src-mode) (not p))
|
||
(org-edit-src-exit))
|
||
((region-active-p)
|
||
(narrow-to-region (region-beginning) (region-end)))
|
||
((derived-mode-p 'org-mode)
|
||
(or (ignore-errors (org-edit-src-code))
|
||
(ignore-errors (org-narrow-to-block))
|
||
(ignore-errors (org-narrow-to-table))
|
||
(org-narrow-to-subtree)))
|
||
((derived-mode-p 'latex-mode)
|
||
(LaTeX-narrow-to-environment))
|
||
((derived-mode-p 'tex-mode)
|
||
(TeX-narrow-to-group))
|
||
(t (narrow-to-defun))))
|
||
|
||
(define-key ctl-x-map (kbd "n t") #'org-narrow-to-table)
|
||
(define-key ctl-x-map (kbd "C-n") #'narrow-or-widen-dwim)
|
||
#+end_src
|
||
** [[info:emacs#Faces][Text faces or styles (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:text-faces-or-styles
|
||
:END:
|
||
|
||
This setup does not configure [[info:emacs#Custom Themes][custom themes (info)]] in order to prevent endless
|
||
useless tweaking. See the [[https://protesilaos.com/codelog/2020-09-05-emacs-note-mixed-font-heights/][note on mixed font heights in Emacs]] for how to setup
|
||
fonts properly. It boils down to two rules:
|
||
1. The height of the default face must be an integer number to make the height a
|
||
physical quantity.
|
||
2. The heights of all other faces must be real numbers to scale those heights
|
||
with respect to the height of the face (those heights default to 1.0 for no
|
||
scaling).
|
||
The code in listing [[lst:configure-face-attributes]] source implements those rules.
|
||
In case of proper initialization of all face heigths, font scaling is easy as
|
||
listing [[lst:my-set-default-face-height]] shows. Finally, the code in listing
|
||
[[lst:my-invert-default-face]] allows swapping the foreground and background colors
|
||
of the default face on all frames.
|
||
|
||
#+caption[Configure =face-attributse=]:
|
||
#+caption: Configure =face-attributes=.
|
||
#+name: lst:configure-face-attributes
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
;; Set face attributes.
|
||
(cond
|
||
((eq system-type 'darwin)
|
||
(set-face-attribute 'default nil :family "Hack" :height 120)
|
||
(set-face-attribute 'fixed-pitch nil :family "Hack")
|
||
(set-face-attribute 'variable-pitch nil :family "FiraGo"))
|
||
((eq system-type 'gnu/linux)
|
||
(set-face-attribute 'default nil :family "Hack" :height 110)
|
||
(set-face-attribute 'fixed-pitch nil :family "Hack")
|
||
(set-face-attribute 'variable-pitch nil :family "FiraGo"))
|
||
(t
|
||
(set-face-attribute 'default nil :family "Hack" :height 110)
|
||
(set-face-attribute 'fixed-pitch nil :family "Hack")
|
||
(set-face-attribute 'variable-pitch nil :family "DejaVu Sans"))))
|
||
#+end_src
|
||
|
||
#+caption[Implement =my-set-default-face-height=]:
|
||
#+caption: Implement =my-set-default-face-height=.
|
||
#+name: lst:my-set-default-face-height
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(defun my-set-default-face-height ()
|
||
"Set the default face height in all current and future frames.
|
||
|
||
Scale all other faces with a height that is a real number."
|
||
(interactive)
|
||
(let* ((prompt (format "face heigth (%s): "
|
||
(face-attribute 'default :height)))
|
||
(choices (mapcar #'number-to-string
|
||
(number-sequence 50 200 10)))
|
||
(height (string-to-number
|
||
(completing-read prompt choices nil 'require-match))))
|
||
(message "Setting the height of the default face to %s" height)
|
||
(set-face-attribute 'default nil :height height))))
|
||
#+end_src
|
||
|
||
#+caption[Implement =my-invert-default-face=]:
|
||
#+caption: Implement =my-invert-default-face=.
|
||
#+name: lst:my-invert-default-face
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(defun my-invert-default-face ()
|
||
"Invert the default face."
|
||
(interactive)
|
||
(invert-face 'default)
|
||
(tweak-region-face-background-color)))
|
||
#+end_src
|
||
|
||
** [[https://jblevins.org/log/rainbow-mode][Visualize color codes and names]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:rainbow-mode
|
||
:END:
|
||
|
||
Listing [[lst:enable-rainbow-mode]] enables =rainbow-mode= to colorize color codes
|
||
and names in buffers for debugging.
|
||
|
||
#+caption[Enable =rainbow-mode=]:
|
||
#+caption: Enable =rainbow-mode=.
|
||
#+name: lst:enable-rainbow-mode
|
||
#+begin_src emacs-lisp
|
||
(when (fboundp 'rainbow-mode)
|
||
(custom-set-variables
|
||
'(rainbow-x-colors-major-mode-list
|
||
'(c++-mode
|
||
c-mode
|
||
emacs-lisp-mode
|
||
inferior-emacs-lisp-mode
|
||
java-mode
|
||
lisp-interaction-mode
|
||
org-mode
|
||
python-mode)))
|
||
(rainbow-mode +1))
|
||
#+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:
|
||
|
||
#+caption[Implement =my-pulse-one-line=]:
|
||
#+caption: Implement =my-pulse-one-line=.
|
||
#+name: lst:my-pulse-one-line
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
;; https://karthinks.com/software/batteries-included-with-emacs/
|
||
;; https://www.reddit.com/r/emacs/comments/jwhr6g/batteries_included_with_emacs/
|
||
(defun my-pulse-one-line (&rest _)
|
||
"Pulse the current line."
|
||
(let ((pulse-iterations 16)
|
||
(pulse-delay 0.1))
|
||
(pulse-momentary-highlight-one-line (point))))
|
||
(dolist (command '(scroll-up-command
|
||
scroll-down-command
|
||
recenter-top-bottom
|
||
other-window))
|
||
(advice-add command :after #'my-pulse-one-line)))
|
||
#+end_src
|
||
|
||
* Applications
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:applications
|
||
:END:
|
||
|
||
** [[https://github.com/skeeto/elfeed#readme][Elfeed: Emacs web feed reader]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-web-feed-reader
|
||
:END:
|
||
|
||
#+caption[Enable =elfeed=]:
|
||
#+caption: Enable =elfeed=.
|
||
#+name: lst:enable-elfeed
|
||
#+begin_src emacs-lisp
|
||
(autoload 'elfeed "elfeed" nil t)
|
||
(global-set-key (kbd "C-x w") #'elfeed)
|
||
|
||
(with-eval-after-load 'elfeed
|
||
(custom-set-variables
|
||
'(elfeed-feeds
|
||
'(("http://www.howardism.org/index.xml" h-abrams)
|
||
("https://ambrevar.xyz/atom.xml" p-neirhardt)
|
||
("https://emacshorrors.com/feed.atom" v-schneidermann)
|
||
("https://emacsninja.com/emacs.atom" v-schneidermann)
|
||
("https://feeds.feedburner.com/InterceptedWithJeremyScahill" j-scahill)
|
||
("https://nullprogram.com/feed/" c-wellons)
|
||
("https://oremacs.com/atom.xml" o-krehel)
|
||
("https://planet.emacslife.com/atom.xml" planet-emacs)
|
||
("https://protesilaos.com/codelog.xml" p-stavrou)
|
||
("https://sachachua.com/blog/category/emacs/feed" s-chua)
|
||
("https://sciencescitoyennes.org/feed/" sciences)
|
||
("https://updates.orgmode.org/feed/updates" org-updates)
|
||
("https://www.aclu.org/taxonomy/feed-term/2152/feed" aclu)
|
||
("https://www.bof.nl/rss/" bof)
|
||
("https://www.democracynow.org/podcast-video.xml" dn)
|
||
("https://www.laquadrature.net/fr/rss.xml" lqdn)
|
||
("https://www.lemonde.fr/blog/huet/feed/" sciences)))))
|
||
#+end_src
|
||
|
||
** [[info:emms#Top][Emacs Multimedia System (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:emacs-multimedia-system
|
||
:END:
|
||
|
||
#+caption[Enable =emms=]:
|
||
#+caption: Enable =emms=.
|
||
#+name: lst:enable-emms
|
||
#+begin_src emacs-lisp
|
||
(custom-set-variables
|
||
'(emms-mode-line-format "")
|
||
'(emms-player-list '(emms-player-mpd emms-player-mpv))
|
||
`(emms-player-mpd-music-directory ,(expand-file-name "~/Music"))
|
||
'(emms-player-mpd-server-name "localhost")
|
||
'(emms-player-mpd-server-port "6600")
|
||
'(emms-player-mpd-verbose t)
|
||
'(emms-playing-time-display-format " %s ")
|
||
'(emms-playlist-mode-center-when-go t))
|
||
|
||
(defun my-emms-print-metadata-find ()
|
||
(require 'find-func)
|
||
(locate-file
|
||
"emms-print-metadata"
|
||
(expand-file-name
|
||
"src"
|
||
(file-name-directory (find-library-name "emms")))
|
||
exec-suffixes #'file-executable-p))
|
||
|
||
(with-eval-after-load 'emms
|
||
(require 'emms-info-libtag)
|
||
(let ((emms-print-metadata (my-emms-print-metadata-find)))
|
||
(when emms-print-metadata
|
||
(custom-set-variables
|
||
'(emms-info-functions nil)
|
||
`(emms-info-libtag-program-name ,emms-print-metadata))
|
||
(add-hook 'emms-info-functions #'emms-info-libtag))))
|
||
|
||
(with-eval-after-load 'elfeed-show
|
||
(when (require 'emms-setup nil 'noerror)
|
||
(emms-all)))
|
||
|
||
(autoload 'emms-streams "emms-streams" nil 'interactive)
|
||
(with-eval-after-load 'emms-streams (emms-all))
|
||
#+end_src
|
||
|
||
* [[info:emacs#Init File][Init File (info)]] footer
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec: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
|
||
(provide 'init)
|
||
|
||
;; Emacs looks for "Local variables:" after the last "?\n?\f".
|
||
|
||
;; Local Variables:
|
||
;; indent-tabs-mode: nil
|
||
;; End:
|
||
;;; init.el ends here
|
||
#+end_src
|
||
|
||
* Local variables linking 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.
|
||
|
||
# Emacs looks for "Local variables:" after the last "\?n\?f".
|
||
|
||
# Local Variables:
|
||
# compile-command: "latexmk -interaction=nonstopmode -lualatex -pvc -shell-escape README.tex"
|
||
# eval: (apply 'my-org-eval-blocks-named '("emacs-lisp-setup"))
|
||
# fill-column: 80
|
||
# End:
|