1469 lines
60 KiB
Org Mode
1469 lines
60 KiB
Org Mode
#+startup: content
|
||
#+title: Emacs setup for use with LaTeX, Org, and Python
|
||
#+author: Gerard Vermeulen
|
||
#+babel: :cache no
|
||
#+property: header-args :tangle init.el :comments link
|
||
#+property: header-args:emacs-lisp :results silent
|
||
#+latex_class: article
|
||
#+latex_class_options: [11pt,a4paper,svgnames]
|
||
#+latex_header: \hypersetup{
|
||
#+latex_header: citecolor=blue,
|
||
#+latex_header: colorlinks=true,
|
||
#+latex_header: filecolor=blue,
|
||
#+latex_header: hyperfootnotes=false,
|
||
#+latex_header: linkcolor=blue,
|
||
#+latex_header: unicode=true,
|
||
#+latex_header: urlcolor=blue
|
||
#+latex_header: }
|
||
#+latex_header: \usepackage{minted}
|
||
#+latex_header: \usemintedstyle{xcode}
|
||
#+latex_header: \usepackage[
|
||
#+latex_header: headheight=20mm,
|
||
#+latex_header: top=40mm,
|
||
#+latex_header: bottom=20mm,
|
||
#+latex_header: left=0.1\paperwidth,
|
||
#+latex_header: right=0.1\paperwidth,
|
||
#+latex_header: heightrounded,
|
||
#+latex_header: verbose,
|
||
#+latex_header: ]{geometry}
|
||
|
||
* Quick start
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:quick-start
|
||
:END:
|
||
|
||
Backup your =~/.emacs.d= directory to execute the following commands:
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src shell :noeval :tangle no
|
||
cd ~
|
||
git clone ccdr@mercury.grenoble.cnrs.fr:SERVER/.emacs.d
|
||
make --directory=.emacs.d init
|
||
#+end_src
|
||
After its invokation, Emacs will install a minimal set of packages. Now, you
|
||
have the option to install all optional packages using the command
|
||
=my-install-optional-packages=, but you can do this any time, or you can install
|
||
any package using the command =package-install= whenever you like. Quit Emacs
|
||
and invoke Emacs again.
|
||
|
||
* 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 my work-flow by showing:
|
||
1. How to tangle (or export) source blocks from [[info:org#Top][org]] files. This file contains
|
||
source blocks to produce the files =early-init.el=, =init.el=, =latexmkrc=, =org-store-link=, and =example.py= by tangling.
|
||
2. How to export [[info:org#Top][org]] files to other formats such as [[https://en.wikipedia.org/wiki/HTML][HTML]], [[https://www.latex-project.org/][LaTeX]], and [[https://en.wikipedia.org/wiki/PDF][PDF]].
|
||
3. How [[info:org#Hyperlinks][org hyperlinks (info)]] allow to link inside and outside [[info:org#Top][Org Mode]]: hover
|
||
over or click on the links to experiment.
|
||
|
||
The [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX - Aalborg University Center TeX]] extension package provides a
|
||
powerful [[https://en.wikipedia.org/wiki/Text-based_user_interface][Text-based User Interface (TUI)]] environment to edit the [[https://www.latex-project.org/][LaTeX]]
|
||
presentations.
|
||
|
||
The [[https://github.com/bdarcus/citar][citar]] extension package provides quick filtering and selecting of
|
||
bibliographic entries, and the option to run different commands on those
|
||
selections. [[https://github.com/bdarcus/citar][Citar]] requires [[info:org#Top][Org-9.5 (info)]], which is already part of Emacs-28.1.
|
||
[[https://github.com/bdarcus/citar][Citar]] exploits the enhancements of Emacs' builtin selection mechanism provided
|
||
by the extension packages [[https://github.com/minad/vertico][vertico]], [[https://github.com/oantolin/orderless][orderless]], [[https://github.com/oantolin/embark][embark]], [[https://github.com/minad/marginalia][marginalia]], and [[https://github.com/minad/consult][consult]].
|
||
The [[https://github.com/andras-simonyi/citeproc-el][citeproc]] extension package provides [[https://citationstyles.org/][CSL: citation style language]] processing
|
||
capabilities to [[https://github.com/bdarcus/citar][citar]] and [[https://orgmode.org/][Org Mode]].
|
||
|
||
The [[https://github.com/vedang/pdf-tools][pdf-tools]] extension package renders [[https://en.wikipedia.org/wiki/PDF][PDF]] file with the possibility to
|
||
annotate the file or to click on anchors in the [[https://en.wikipedia.org/wiki/PDF][PDF]] file that link back to the
|
||
original [[https://www.latex-project.org/][LaTeX]] file of the document. An example of my work-flow are the steps
|
||
to convert this [[info:org#Top][org]] file to [[https://en.wikipedia.org/wiki/PDF][PDF]] and to see the result with [[https://github.com/vedang/pdf-tools][pdf-tools]] in Emacs:
|
||
execute the commands ~pdf-tools-install~, ~org-babel-tangle~,
|
||
~org-latex-export-latex-to-latex~, and ~compile~. This sets up an infinite
|
||
[[https://www.latex-project.org/][LaTeX]] compilation loop to update and redisplay the [[https://en.wikipedia.org/wiki/PDF][PDF]] file after excution of
|
||
the ~org-latex-export-latex-to-latex~ command in this buffer.
|
||
|
||
Here follows a list of interesting Emacs configurations:
|
||
1. [[https://github.com/alhassy/emacs.d][Musa Al-hassy's configuration]] is an impressive example of producing the Emacs
|
||
initialization files and other files by tangling an [[info:org#Top][org]] file. His methodology
|
||
is impressive, as his [[https://alhassy.github.io/ElispCheatSheet/][Elisp Cheat Sheet]] and [[https://alhassy.github.io/org-special-block-extras/][org-special-block-extra package]]
|
||
show. To me, this is a configuration to admire, but his methodology is way
|
||
over my head.
|
||
2. [[https://github.com/oantolin/emacs-config][Omar Antolín Camarena's configuration]] exploits built-in packages, Omar's own
|
||
small packages, and large external packages. Omar is the author of [[https://github.com/oantolin/orderless][orderless]]
|
||
and [[https://github.com/oantolin/embark][embark]]. I have stolen his idea of using ~custom-set-variables~.
|
||
3. [[https://gitlab.com/ambrevar/dotfiles][Pierre Neirhardt's configuration]] implements lazy loading without help of
|
||
external packages. I have stolen his approach of using lazy loading to
|
||
silently ignore the setup stanzas of uninstalled extension packages.
|
||
4. [[https://sachachua.com/dotemacs/][Sacha Chua's configuration]] is a practical example of producing the Emacs
|
||
initialization files by tangling an [[info:org#Top][org]] file. It gives me the impression
|
||
that she is a very practical person trying to achieve her goals by the most
|
||
efficient means. I have stolen her idea of using [[https://github.com/quelpa/quelpa][quelpa]] to install packages
|
||
from any source.
|
||
5. [[https://github.com/purcell/emacs.d][Steve Purcell's configuration]] is well organized, a showcase of readable code,
|
||
as well helpful commit and issue histories. See for instance the discussion
|
||
on [[https://github.com/purcell/emacs.d/issues/778][the correctness of order of company candidates in Emacs lisp mode]].
|
||
6. [[https://github.com/tecosaur/emacs-config][Timothy E. Chapman's configuration]]
|
||
|
||
* [[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.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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, move point (or cursor) to one of the items of the
|
||
list the and type =C-c C-c=:
|
||
1. src_emacs-lisp[:exports code]{(describe-variable #'load-prefer-newer t)}
|
||
2. src_emacs-lisp[:exports code]{(apropos-library "no-littering")}
|
||
3. src_emacs-lisp[:exports code]{(find-function #'hack-local-variables)}
|
||
to execute the code between the curly braces for access to help.
|
||
|
||
This shows why *Emacs is a self-documenting editor.*
|
||
|
||
* [[info:emacs#Init File][Init File (info)]] header
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:init-file-header
|
||
:END:
|
||
|
||
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-variable~ function call below. A tutorial of how to use those
|
||
reader macros is the [[https://mullikine.github.io/posts/macro-tutorial/][didactic emacs-lisp macro example]].
|
||
|
||
Because of the ~custom-set-variable~ function call, the [[info:emacs#Init File][init file (info)]] does
|
||
not load the ~custom-file~ as [[info:emacs#Saving Customizations][saving customizations (info)]] recommends.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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 ,(make-temp-file "emacs-custom-"))
|
||
'(epg-pinentry-mode 'loopback)
|
||
'(global-hl-line-mode t)
|
||
'(global-hl-line-sticky-flag t)
|
||
'(history-delete-duplicates t)
|
||
'(history-length 500)
|
||
'(indent-tabs-mode nil)
|
||
'(inhibit-startup-buffer-menu t)
|
||
'(inhibit-startup-screen t)
|
||
'(initial-buffer-choice t)
|
||
'(initial-scratch-message "")
|
||
`(insert-directory-program ,(or (executable-find "gls")
|
||
(executable-find "ls")))
|
||
'(kill-ring-max 300)
|
||
'(package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
|
||
("nongnu" . "https://elpa.nongnu.org/nongnu/")
|
||
("melpa" . "https://melpa.org/packages/")))
|
||
`(package-selected-packages
|
||
`(
|
||
,@(when (version< emacs-version "28.0")
|
||
'(
|
||
org ; plain text thought organizer
|
||
modus-themes ; high foreground/background contrast themes
|
||
))
|
||
anaconda-mode ; strangles python-mode
|
||
async ; asynchroneous processing
|
||
auctex ; Aalborg University Center TeX
|
||
blacken ; Black Python-code formatter client
|
||
citar ; bibliography handling
|
||
citeproc ; bibliography handling
|
||
company ; complete anything
|
||
company-anaconda ; complete anything in anaconda-mode
|
||
consult ; consult completing-read
|
||
eglot ; Emacs polyGLOT LSP client
|
||
electric-operator ; automatic spacing around operators
|
||
elfeed ; web feed reader
|
||
embark ; act on any buffer selection
|
||
emms ; Emacs Multi-Media System
|
||
htmlize ; convert buffer contents to HTML
|
||
iedit ; simultaneous multi-entity editing
|
||
laas ; LaTeX Auto-Activating Snippets
|
||
leuven-theme ; beautiful color theme
|
||
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
|
||
smartparens ; smart editing of character pairs
|
||
toml-mode ; Tom's Obvious Minimal Language mode
|
||
vertico ; VERTical Interactive Completion
|
||
wgrep ; open a writable grep buffer
|
||
which-key ; on the fly key-binding help
|
||
wordnut ; WordNet lexical database
|
||
writegood-mode ; bullshit and weasel-word detector
|
||
ws-butler ; remove trailing whitespace
|
||
xr ; undo rx to grok regular expressions
|
||
yasnippet ; code or text template expansion
|
||
))
|
||
'(python-indent-guess-indent-offset nil)
|
||
'(recentf-mode t)
|
||
'(save-place-mode t)
|
||
'(savehist-additional-variables
|
||
'(eww-history
|
||
kill-ring
|
||
regexp-search-string
|
||
search-ring
|
||
search-string))
|
||
'(savehist-mode t)
|
||
'(savehist-save-minibuffer-history 1)
|
||
'(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
|
||
|
||
* Package bootstrapping
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:package-bootstrapping
|
||
: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)]].
|
||
Finally, 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 assumes that the package system is in a *virgin* state in case the
|
||
package [[https://github.com/emacscollective/no-littering][no-littering]] is not present. Refreshing the contents of available
|
||
packages at least once is a requirement in order to be able to install and load
|
||
any packages, hence also [[https://github.com/emacscollective/no-littering][no-littering]].
|
||
|
||
The call src_emacs-lisp[:exports code]{(package-install-selected-packages)}
|
||
checks the installation status of all packages in
|
||
src_emacs-lisp[:exports code]{package-selected-packages} and installs the
|
||
missing packages after the user has agreed to its prompt.
|
||
|
||
After package bootstrapping, you have to refresh the list of available packages
|
||
yourself before updating the installed packages.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
;; The is the 1st package bootstrapping block.
|
||
(unless (require 'no-littering nil 'noerror)
|
||
(package-refresh-contents)
|
||
(package-install 'no-littering)
|
||
(require 'no-littering))
|
||
|
||
(unless noninteractive
|
||
(package-install-selected-packages))
|
||
#+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=.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(when window-system
|
||
(unless (or noninteractive (daemonp))
|
||
(add-hook 'after-init-hook #'server-start)))
|
||
#+end_src
|
||
|
||
The next two configuration blocks 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.
|
||
|
||
** LaTeX save-compile-display loop
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:latex-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 bgcolor=LightGoldenrodYellow
|
||
#+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 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.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+header: :comments none
|
||
#+header: :tangle-mode (identity #o755)
|
||
#+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]] fills another
|
||
niche than the five packages above.
|
||
|
||
** [[info:vertico#Top][Vertico (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:vertico-configuration
|
||
:END:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (fboundp 'vertico-mode)
|
||
(vertico-mode +1)))
|
||
#+end_src
|
||
|
||
** [[info:orderless#Top][Orderless (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:orderless-configuration
|
||
:END:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (cl-every #'fboundp '(embark-act embark-bindings embark-dwim))
|
||
(global-set-key (kbd "C-,") #'embark-act)
|
||
(global-set-key (kbd "C-:") #'embark-dwim)
|
||
(global-set-key (kbd "C-h B") #'embark-bindings)))
|
||
#+end_src
|
||
|
||
** [[info:marginalia#Top][Marginalia (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:marginalia-configuration
|
||
:END:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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)
|
||
(global-set-key (kbd "C-x M-:") #'consult-complex-command)
|
||
(global-set-key (kbd "C-x b") #'consult-buffer)
|
||
(global-set-key (kbd "C-x 4 b") #'consult-buffer-other-window)
|
||
(global-set-key (kbd "C-x 5 b") #'consult-buffer-other-frame)
|
||
(global-set-key (kbd "C-x r x") #'consult-register)
|
||
(global-set-key (kbd "C-x r b") #'consult-bookmark)
|
||
;; M-g bindings (goto-map)
|
||
(global-set-key (kbd "M-g g") #'consult-goto-line)
|
||
(global-set-key (kbd "M-g M-g") #'consult-goto-line)
|
||
(global-set-key (kbd "M-g o") #'consult-outline)
|
||
(global-set-key (kbd "M-g m") #'consult-mark)
|
||
(global-set-key (kbd "M-g k") #'consult-global-mark)
|
||
(global-set-key (kbd "M-g i") #'consult-imenu-project)
|
||
(global-set-key (kbd "M-g e") #'consult-error)
|
||
;; M-s bindings (search-map)
|
||
(global-set-key (kbd "M-s g") #'consult-git-grep)
|
||
(global-set-key (kbd "M-s f") #'consult-find)
|
||
(global-set-key (kbd "M-s k") #'consult-keep-lines)
|
||
(global-set-key (kbd "M-s l") #'consult-line)
|
||
(global-set-key (kbd "M-s m") #'consult-multi-occur)
|
||
(global-set-key (kbd "M-s 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:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
python-mode-hook
|
||
ielm-mode-hook))
|
||
(add-hook hook #'company-mode))))
|
||
#+end_src
|
||
|
||
** Minibuffer history completion
|
||
|
||
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.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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)
|
||
|
||
;; Adapted from ‘minibuffer-complete’:
|
||
(defun my-minibuffer-complete-history ()
|
||
(interactive)
|
||
(completion-in-region (minibuffer--completion-prompt-end) (point-max)
|
||
(symbol-value minibuffer-history-variable)
|
||
nil))
|
||
|
||
(define-key minibuffer-local-map [C-tab] 'my-minibuffer-complete-history)
|
||
#+end_src
|
||
|
||
* Reading
|
||
|
||
** EPUB files
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(when (fboundp 'nov-mode)
|
||
(add-to-list 'auto-mode-alist `(,(rx ".epub" eos) . nov-mode)))
|
||
#+end_src
|
||
|
||
** PDF files
|
||
|
||
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.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(when (fboundp 'pdf-tools-install)
|
||
(autoload 'pdf-view-mode "pdf-view")
|
||
(add-to-list 'magic-mode-alist '("%PDF" . pdf-view-mode)))
|
||
|
||
(with-eval-after-load 'pdf-tools
|
||
(when (fboundp 'pdf-view-restore-mode)
|
||
(add-hook 'pdf-view-mode-hook #'pdf-view-restore-mode)))
|
||
#+end_src
|
||
|
||
* Writing
|
||
|
||
** LaTeX
|
||
|
||
Loading =tex.el= immediately instead of lazily ensures proper initialization of
|
||
the [[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~.
|
||
|
||
Out of the box, [[https://en.wikipedia.org/wiki/AUCTeX][AUCTeX]] does not indent text between square brackets. The code
|
||
below corrects this by advising to override ~TeX-brace-count-line~ with
|
||
~my-TeX-brace-count-line~.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
;; Try to get the `safe-local-variable' predicate for `TeX-master'
|
||
(when (require 'tex nil 'noerror)
|
||
(custom-set-variables
|
||
'(LaTeX-section-hook '(LaTeX-section-heading
|
||
LaTeX-section-title
|
||
LaTeX-section-toc
|
||
LaTeX-section-section
|
||
LaTeX-section-label))
|
||
'(TeX-auto-save t)
|
||
'(TeX-parse-self t)
|
||
'(font-latex-fontify-sectioning 1.0))
|
||
;; 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]]
|
||
|
||
** Org-mode
|
||
|
||
*** [[info:org#Activation][Activation (info)]]
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
*** Customization
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(custom-set-variables
|
||
'(org-babel-python-command "python -E")
|
||
'(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)
|
||
(scheme . t)
|
||
(shell . t)))
|
||
'(org-cite-export-processors '((latex biblatex)
|
||
(t csl)))
|
||
'(org-cite-global-bibliography '("~/VCS/research/refs.bib"))
|
||
'(org-file-apps '((auto-mode . emacs)
|
||
(directory . emacs)
|
||
("\\.mm\\'" . default)
|
||
("\\.x?html?\\'" . default)
|
||
("\\.pdf\\'" . emacs)))
|
||
'(org-confirm-babel-evaluate nil)
|
||
'(org-latex-compiler "lualatex")
|
||
'(org-latex-hyperref-template nil)
|
||
'(org-latex-listings 'minted)
|
||
'(org-latex-logfiles-extensions '("blg" "lof" "log" "lot" "out" "toc"))
|
||
'(org-latex-prefer-user-labels t)
|
||
'(org-modules '(ol-bibtex
|
||
ol-doi
|
||
ol-eww
|
||
ol-info
|
||
org-id
|
||
org-protocol
|
||
org-tempo))
|
||
'(org-src-fontify-natively t)
|
||
'(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
|
||
|
||
*** [[info:org#Citation export processors][Citation export processors (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:citation-export-processors
|
||
:END:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'oc
|
||
(require 'oc-biblatex)
|
||
(require 'oc-csl))
|
||
#+end_src
|
||
|
||
*** [[info:org#External Links][Export external links (info)]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:export-external-links
|
||
:END:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
"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
|
||
|
||
In order to export the [[info:org#External Links][info links (info)]] in this document to =html= correctly,
|
||
modify the constant ~org-info-other-documents~ defined in =ol-info.el=.
|
||
|
||
Note: how to make it work without tangling?
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(add-hook
|
||
'org-mode-hook
|
||
(lambda ()
|
||
(when (boundp 'org-info-other-documents)
|
||
(dolist (recipe '(("consult" . "https://github.com/minad/consult")
|
||
("embark" . "https://github.com/oantolin/embark")
|
||
("marginalia" . "https://github.com/minad/marginalia")
|
||
("orderless" . "https://github.com/oantolin/orderless")
|
||
("vertico" . "https://github.com/minad/vertico")))
|
||
(cl-pushnew recipe 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)]]
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
*** [[info:org#Advanced Export Configuration][Advanced Export Configuration (info)]]
|
||
|
||
Stolen from [[https://git.sr.ht/~bzg/org-contrib/tree/master/item/lisp/ox-extra.el][ox-extra.el]]
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ox
|
||
(defun my-org-export-ignore-headline-filter (_)
|
||
"Ignore all headlines with the \":ignore:\" tag."
|
||
(org-map-entries
|
||
(lambda ()
|
||
(delete-region (point) (line-beginning-position 2)))
|
||
":ignore:"))
|
||
|
||
(defun my-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)))))
|
||
|
||
(defun my-activate-buffer-local-org-export-filters ()
|
||
"Activate my export filters locally in the current buffer."
|
||
(interactive)
|
||
(add-hook 'org-export-before-parsing-hook
|
||
#'my-org-export-ignore-headline-filter nil 'local)
|
||
(add-hook 'org-export-before-parsing-hook
|
||
#'my-org-latex-header-blocks-filter nil 'local)))
|
||
#+end_src
|
||
|
||
#+name: emacs-lisp-setup
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp :tangle no
|
||
(with-eval-after-load 'ox
|
||
(my-activate-buffer-local-org-export-filters))
|
||
#+end_src
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ox-latex
|
||
(mapc (lambda (item)
|
||
(add-to-list 'org-latex-classes item))
|
||
'(;; The postfixes +1, +2, +3, -1, -2, and -3 denote:
|
||
;; +1 => [DEFAULT-PACKAGES]
|
||
;; +2 => [PACKAGES]
|
||
;; +3 => [EXTRA]
|
||
;; -1 => [NO-DEFAULT-PACKAGES]
|
||
;; -2 => [NO-PACKAGES]
|
||
;; -3 => [NO-EXTRA]
|
||
("elsarticle-1+2+3" ; Elsevier journals
|
||
"\\documentclass{elsarticle}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[PACKAGES]
|
||
[EXTRA]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
|
||
("article-1+2+3"
|
||
"\\documentclass{article}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[PACKAGES]
|
||
[EXTRA]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
|
||
("report-1+2+3"
|
||
"\\documentclass[11pt]{report}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[PACKAGES]
|
||
[EXTRA]"
|
||
("\\part{%s}" . "\\part*{%s}")
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
("book-1+2+3"
|
||
"\\documentclass[11pt]{book}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[PACKAGES]
|
||
[EXTRA]"
|
||
("\\part{%s}" . "\\part*{%s}")
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")))))
|
||
#+end_src
|
||
|
||
*** Evaluate source blocks on loading
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(defun my-org-eval-blocks-named (name)
|
||
"Evaluate all source blocks named NAME."
|
||
(when (eq major-mode 'org-mode)
|
||
(let ((blocks
|
||
(org-element-map
|
||
(org-element-parse-buffer 'greater-element nil) 'src-block
|
||
(lambda (block)
|
||
(when (string= name (org-element-property :name block))
|
||
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
|
||
|
||
** Citing bibliography
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:citing-bibliography
|
||
:END:
|
||
|
||
[[https://github.com/bdarcus/citar][Citar]] provides a completing-read front-end to browse and act on BibTeX,
|
||
BibLaTeX, and CSL JSON bibliographic data, and LaTeX, markdown, and org-cite
|
||
editing support.
|
||
|
||
[[https://github.com/bdarcus/citar][Citar]] -- in combination with vertico, embark, and marginalia -- provides quick
|
||
filtering and selecting of bibliographic entries from the minibuffer, and the
|
||
option to run different commands on those selections.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(when (cl-every #'fboundp '(citar-insert-citation
|
||
citar-insert-preset
|
||
citar-org-activate
|
||
citar-org-follow
|
||
citar-org-insert))
|
||
(custom-set-variables
|
||
'(org-cite-activate-processor 'citar)
|
||
'(org-cite-follow-processor 'citar)
|
||
'(org-cite-insert-processor 'citar))
|
||
(global-set-key (kbd "C-c b") #'citar-insert-citation)
|
||
(define-key minibuffer-local-map (kbd "M-b") #'citar-insert-preset))
|
||
#+end_src
|
||
|
||
* Editing
|
||
|
||
** [[https://www.emacswiki.org/emacs/DisabledCommands][Enable disabled commands and inform]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:enable-disabled-commands
|
||
:END:
|
||
|
||
Execute src_emacs-lisp[:exports code]{(find-library "novice")} to see how
|
||
Emacs prevents new users from shooting themselves in the feet.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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#Narrowing][Narrowing]]
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:narrowing
|
||
:END:
|
||
|
||
[[https://endlessparentheses.com/emacs-narrow-or-widen-dwim.html][Emacs narrow-or-widen-dwim]]
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(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))
|
||
(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 "C-n") #'narrow-or-widen-dwim)
|
||
#+end_src
|
||
|
||
** Synchronal multiple-region editing
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(require 'iedit nil 'noerror))
|
||
#+end_src
|
||
|
||
** Extraneous whitespace trimming
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
** Smart character-pair handling
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
(when (require 'smartparens-config nil 'noerror)
|
||
;; Requiring smartparens-config disables pairing of the quote
|
||
;; character for lisp modes, contrary to requiring smartparens.
|
||
(custom-set-variables
|
||
'(sp-base-key-bindings 'sp)
|
||
'(sp-override-key-bindings '(("C-(" . sp-backward-barf-sexp)
|
||
("C-)" . sp-forward-slurp-sexp))))
|
||
|
||
(add-hook 'prog-mode-hook #'turn-on-smartparens-mode)
|
||
(add-hook 'text-mode-hook #'turn-on-smartparens-mode)
|
||
|
||
(dolist (hook '(emacs-lisp-mode-hook
|
||
eval-expression-minibuffer-setup-hook
|
||
ielm-mode-hook
|
||
python-mode-hook))
|
||
(add-hook hook #'turn-on-smartparens-mode))
|
||
|
||
;; 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))
|
||
|
||
(sp-local-pair 'prog-mode "(" nil :post-handlers '((indent-between-pair "RET")))
|
||
(sp-local-pair 'prog-mode "[" nil :post-handlers '((indent-between-pair "RET")))
|
||
(sp-local-pair 'prog-mode "{" nil :post-handlers '((indent-between-pair "RET")))
|
||
|
||
(show-smartparens-global-mode +1)))
|
||
#+end_src
|
||
|
||
** Electric operators
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: sec:electric-operators
|
||
:END:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
** Smart snippets
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
* Coding
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'eglot
|
||
(add-to-list 'eglot-server-programs '(python-mode "pylsp"))
|
||
(setq-default
|
||
eglot-workspace-configuration
|
||
`((:pylsp . (:plugins (:jedi_completion (:eager nil))))
|
||
(:pylsp . (:plugins (:jedi_completion (:cache_for ,(vconcat '("astropy"
|
||
"numpy"
|
||
"scipy")))))))))
|
||
#+end_src
|
||
|
||
** Python coding
|
||
|
||
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/pythonic-emacs/anaconda-mode][Anaconda - code navigation, documentation lookup, and completion for Python]].
|
||
2. [[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.
|
||
|
||
The snippet below initializes [[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 and the call to [[info:elisp#Advising Functions][advice-add]] opens
|
||
Python org-mode edit-buffers in ~anaconda-mode~.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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)
|
||
(set (make-local-variable '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)
|
||
(set (make-local-variable '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))))))
|
||
|
||
(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 ()
|
||
(interactive)
|
||
(if (bound-and-true-p anaconda-mode)
|
||
(my-disable-anaconda-mode)
|
||
(my-enable-anaconda-mode)))))
|
||
#+end_src
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src python :tangle example.py :comments link
|
||
import numpy
|
||
import astropy.units as apu
|
||
|
||
a = numpy.arange(0, 11)
|
||
a = numpy.linspace(0, 10, num=11)
|
||
q = apu.Quantity(a, apu.meter)
|
||
print(q)
|
||
#+end_src
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(custom-set-variables
|
||
'(python-shell-interpreter-args "-i -E"))
|
||
(when (and (executable-find "pyenv")
|
||
(require 'pyenv-mode nil 'noerror))
|
||
(pyenv-mode +1)
|
||
(pyenv-mode-set "3.9.9/envs/python-3.9.9"))
|
||
#+end_src
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'info
|
||
(add-to-list 'Info-directory-list
|
||
(expand-file-name "~/.local/share/info")))
|
||
#+end_src
|
||
|
||
Look into:
|
||
1. [[https://github.com/douglasdavis/numpydoc.el/blob/main/numpydoc.el][Emacs extension to insert numpy style docstrings in function definitions]]
|
||
|
||
* Appearance
|
||
|
||
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 next source code blocks implement those rules.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
In case of proper initialization of all face heigths, font scaling is easy as
|
||
the next source code block shows.
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(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
|
||
|
||
This setup prefers the ~leuven~ and ~leuven-dark~ themes from [[https://melpa.org/#/][MELPA]], because the
|
||
very popular ~modus-operandi~ and ~modus-vivendi~ themes feel quirky: for
|
||
instance those themes fail to display ~hl-line-mode~ properly with Emacs-27.2 on
|
||
Darwin.
|
||
|
||
[[https://emacs.stackexchange.com/questions/17431/how-do-i-change-portions-of-a-custom-theme][How to change custom theme faces]]
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp
|
||
(unless noninteractive
|
||
;; Try to detect `leuven-theme` from MELPA.
|
||
(when (fboundp 'leuven-scale-font)
|
||
(custom-set-variables
|
||
'(leuven-scale-org-agenda-structure 1.0)
|
||
'(leuven-scale-org-document-title 1.0)
|
||
'(leuven-scale-outline-headlines 1.0)
|
||
'(leuven-scale-volatile-highlight 1.0)))
|
||
|
||
(defun my-leuven-hook-function ()
|
||
(when (member 'leuven custom-enabled-themes)
|
||
(let ((custom-inhibit--theme-enable nil)
|
||
(ol1 (list :height 1.0 :weight 'bold
|
||
:foreground "#3C3C3C" :background "#F0F0F0"))
|
||
(ol2 (list :height 1.0 :weight 'bold
|
||
:foreground "#123555" :background "#E5F4FB")))
|
||
(custom-theme-set-faces
|
||
'leuven
|
||
`(font-latex-sectioning-2-face ((t ,ol1)))
|
||
`(font-latex-sectioning-3-face ((t ,ol2)))
|
||
`(info-title-1 ((t ,ol1)))
|
||
`(markdown-header-face-1 ((t ,ol1)))
|
||
`(markdown-header-face-2 ((t ,ol2)))
|
||
`(org-level-1 ((t ,ol1)))
|
||
`(org-level-2 ((t ,ol2)))
|
||
`(outline-1 ((t ,ol1)))
|
||
`(outline-2 ((t ,ol1)))))
|
||
(enable-theme 'leuven)))
|
||
|
||
(load-theme 'leuven 'no-confirm nil)
|
||
|
||
(dolist (hook '(Info-mode-hook
|
||
latex-mode-hook
|
||
markdown-mode-hook
|
||
org-mode-hook
|
||
outline-mode-hook))
|
||
(add-hook hook #'my-leuven-hook-function)))
|
||
#+end_src
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+begin_src emacs-lisp :tangle no
|
||
(unless noninteractive
|
||
(custom-set-variables
|
||
'(modus-themes-hl-line 'underline)
|
||
'(modus-themes-intense-markup 't))
|
||
(when (and (version< emacs-version "28.0")
|
||
(require 'modus-themes nil 'noerror))
|
||
(modus-themes-load-themes)))
|
||
#+end_src
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
** Feed reader
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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
|
||
|
||
** Multi-media system
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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:init-file-footer
|
||
:END:
|
||
|
||
#+attr_latex: :options bgcolor=LightGoldenrodYellow
|
||
#+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 back to [[#sec:latex-save-compile-display-loop][LaTeX 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:
|