2295 lines
86 KiB
Org Mode
2295 lines
86 KiB
Org Mode
#+title: GNU Emacs configuration
|
|
#+author: Vedang Manerikar
|
|
#+email: vedang@unravel.tech
|
|
#+language: en
|
|
#+options: ':t toc:nil num:t author:t email:t
|
|
|
|
This configuration is inspired from the work of [[https://github.com/protesilaos/dotfiles/blob/master/emacs/.emacs.d/prot-emacs.org][my hero Prot]]. I've copied straight from his config file, because I think the explanations he has created are worthwhile and should be read by everyone. Prot's files are all prefixed with =prot-emacs-*=. I have changed this to =unravel-*=. The reason for this is that I have picked up only what I need and changed it where needed. As such, any issues you face with this configuration are likely introduced by me.
|
|
|
|
Any quote without attribution is directly taken from [[https://github.com/protesilaos/dotfiles/blob/master/emacs/.emacs.d/prot-emacs.org][Prot's org file]]. You should read through it if you want detailed explanations of things you find here.
|
|
|
|
#+begin_quote
|
|
What you are now reading is not a common literate configuration of
|
|
Emacs. In most such cases, you have a generic =init.el= with a call to
|
|
the ~org-babel-load-file~ function that gets an Org document as its
|
|
value. That method works but is very slow, because we have to load Org
|
|
before starting Emacs (and Org loads a bunch of other things we do not
|
|
need at such an early stage).
|
|
|
|
Whereas this Org document serves as (i) a single point of entry to my
|
|
Emacs setup and (ii) the origin of all of my Emacs configurations.
|
|
While I am defining everything in a single Org file, I am not actually
|
|
starting Emacs by reading this file. Rather, I am instructing Org to
|
|
put the code blocks defined herein in standalone files, organised by
|
|
scope. The end result is something where you cannot tell whether a
|
|
literate program was executed or not.
|
|
|
|
This is the beauty of it. I can keep editing a single file as the
|
|
"source of truth", though I can still handle each of the files
|
|
individually (e.g. someone wants to see how I do a specific thing, so
|
|
I share only that file as an email attachment---no need to send over
|
|
this massive document).
|
|
|
|
When I want to modify my Emacs setup, I edit this file and then
|
|
evaluate the following code block or do =C-c C-v C-t=. All files will
|
|
be updated accordingly.
|
|
#+end_quote
|
|
|
|
#+src emacs-lisp :tangle no :results none
|
|
(org-babel-tangle)
|
|
#+end_src
|
|
|
|
#+toc: headlines 2
|
|
|
|
Here is what the generated directory structure should look like:
|
|
|
|
#+begin_src sh :dir ~/src/prototypes/emacs-up :results raw
|
|
fd -e el -e org -E elpa | tree --fromfile
|
|
#+end_src
|
|
|
|
#+RESULTS:
|
|
.
|
|
├── early-init.el
|
|
├── init.el
|
|
├── unravel-emacs.org
|
|
└── unravel-modules
|
|
├── unravel-completion.el
|
|
├── unravel-essentials.el
|
|
├── unravel-langs.el
|
|
└── unravel-theme.el
|
|
|
|
2 directories, 7 files
|
|
|
|
To make a change to this Emacs configuration, edit this file and then type =C-c C-v C-t= (=M-x org-babel-tangle=) to republish all the relevant files.
|
|
|
|
* The ~early-init.el~ file
|
|
|
|
#+begin_quote
|
|
This is the first file that Emacs reads when starting up. It should
|
|
contain code that does not depend on any package or the proportions of
|
|
the Emacs frame. In general, this early initialisation file is meant
|
|
to set up a few basic things before Emacs produces the initial frame
|
|
by delegating to the =init.el
|
|
#+end_quote
|
|
|
|
** The =early-init.el= basic frame settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:a1288a07-93f6-4e14-894e-707d5ad8b6dc
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "early-init.el"
|
|
;;;; No GUI
|
|
;; I do not use those graphical elements by default, but I do enable
|
|
;; them from time-to-time for testing purposes or to demonstrate
|
|
;; something. NEVER tell a beginner to disable any of these. They
|
|
;; are helpful.
|
|
(menu-bar-mode -1)
|
|
(scroll-bar-mode -1)
|
|
(tool-bar-mode -1)
|
|
#+end_src
|
|
|
|
** The =early-init.el= tweaks to startup time and garbage collection
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:50d28f3c-3ada-4db5-b830-bbbbee7fec4e
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "early-init.el"
|
|
;; A big contributor to startup times is garbage collection.
|
|
|
|
;; We up the gc threshold to temporarily prevent it from running, then
|
|
;; reset it later by enabling `gcmh-mode'. Not resetting it will cause
|
|
;; stuttering/freezes.
|
|
|
|
(setq gc-cons-threshold most-positive-fixnum)
|
|
#+end_src
|
|
|
|
** If you have both .el and .elc files, load the newer one
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:F8987E20-3E36-4E27-9EAE-D0680303A95B
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "early-init.el"
|
|
;; When both .el and .elc / .eln files are available,
|
|
;; load the latest one.
|
|
|
|
(setq load-prefer-newer t)
|
|
#+end_src
|
|
|
|
** The =early-init.el= initialises the package cache
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:7a037504-8a2f-4df0-8482-ce6476354440
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "early-init.el"
|
|
;; Ensure that `describe-package' does not require a
|
|
;; `package-refresh-contents'.
|
|
(setq package-enable-at-startup t)
|
|
#+end_src
|
|
|
|
** The =early-init.el= gives a name to the default frame
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:ad227f7e-b0a7-43f8-91d6-b50db82da9ad
|
|
:END:
|
|
|
|
Naming frames allows you to select them using completion (=M-x select-frame-by-name=).
|
|
|
|
#+begin_src emacs-lisp :tangle "early-init.el"
|
|
;; Name the default frame
|
|
;; You can select a frame with M-x select-frame-by-name
|
|
(add-hook 'after-init-hook (lambda () (set-frame-name "unravel")))
|
|
#+end_src
|
|
|
|
* The ~init.el~ file
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:dae63bd9-93a8-41c4-af1b-d0f39ba50974
|
|
:END:
|
|
|
|
This is the main initialisation file of Emacs. Everything loads from here, even if it has been split into multiple files for convenience.
|
|
|
|
** The =init.el= tweaks to make native compilation silent
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:3563ceb5-b70c-4191-9c81-f2f5a202c4da
|
|
:END:
|
|
|
|
These warnings are unnecessarily scary.
|
|
|
|
#+begin_quote
|
|
The =--with-native-compilation=yes= build option of Emacs is very
|
|
nice: it enables the "native compilation" of Emacs Lisp, translating
|
|
it down to machine code. However, the default setting for reporting
|
|
errors is set to a verbose value which, in my coaching experience,
|
|
confuses users: it produces warnings for compilation issues that only
|
|
the developer of the given package needs to deal with. These include
|
|
innocuous facts like docstrings being wider than a certain character
|
|
count. To make things even worse, the buffer that shows these warnings
|
|
uses the stop sign character, resulting in a long list of lines with
|
|
red spots everywhere, as if we have totally broken Emacs.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
;; Make native compilation silent and prune its cache.
|
|
(when (native-comp-available-p)
|
|
(setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation
|
|
(setq native-compile-prune-cache t)) ; Emacs 29
|
|
#+end_src
|
|
|
|
** The =init.el= setting to send ~custom-file~ to oblivion
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:f2ffe0e9-a58d-4bba-9831-cc35940ea83f
|
|
:END:
|
|
|
|
There is no need to use the =M-x customize= infrastructure. It's easier to just rely on the init file instead.
|
|
|
|
#+begin_quote
|
|
I would prefer to just have an option to avoid the Custom
|
|
infrastructure altogether, but this is not possible. So here we are...
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
;; Disable custom.el by making it disposable.
|
|
(setq custom-file (make-temp-file "emacs-custom-"))
|
|
#+end_src
|
|
|
|
** The =init.el= settings to enable commands disabled by default
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:4ed6593f-6f55-4258-a1c2-ddb50e9e2465
|
|
:END:
|
|
|
|
These commands are actually useful, especially in org-mode.
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
;; Enable these commands which have been disabled by default
|
|
(mapc
|
|
(lambda (command)
|
|
(put command 'disabled nil))
|
|
'(list-timers narrow-to-region narrow-to-page upcase-region downcase-region))
|
|
#+end_src
|
|
|
|
** The =init.el= settings to disable unnecessary commands enabled by default
|
|
|
|
These commands are "unsafe", in that we should be using the alternatives (like ~vterm~ and ~org~)
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
;; Disable these commands which have been enabled by default
|
|
(mapc
|
|
(lambda (command)
|
|
(put command 'disabled t))
|
|
'(eshell project-eshell overwrite-mode iconify-frame diary))
|
|
#+end_src
|
|
|
|
** Add the modules folder to the load-path
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:e289a614-4f17-4d6c-a028-42fe45aebe66
|
|
:END:
|
|
|
|
This is where all the custom configuration sits for all the packages we use. We write configuration on a per-file basis instead of in a giant file, because these smaller files are more readable, approachable and shareable.
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
(mapc
|
|
(lambda (string)
|
|
(add-to-list 'load-path (locate-user-emacs-file string)))
|
|
'("unravel-modules"))
|
|
#+end_src
|
|
|
|
** The =init.el= settings for packages (=package.el=)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:424340cc-f3d7-4083-93c9-d852d40dfd40
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The =package.el= is built into Emacs and is perfectly fine for my
|
|
use-case. We do not need to load it explicitly, as it will be called
|
|
by ~use-package~ when it needs it. Since the introduction of the
|
|
=early-init.el= file, we also do not need to initialise the packages
|
|
at this point: we activate the cache instead ([[#h:7a037504-8a2f-4df0-8482-ce6476354440][The =early-init.el= initialises the package cache]]).
|
|
|
|
With regard to the settings here, make sure to read my article about
|
|
package archives, pinning packages, and setting priorities:
|
|
<https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
;;;; Packages
|
|
|
|
(setq package-vc-register-as-project nil) ; Emacs 30
|
|
|
|
(add-hook 'package-menu-mode-hook #'hl-line-mode)
|
|
|
|
;; Also read: <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>
|
|
(setq package-archives
|
|
'(("gnu-elpa" . "https://elpa.gnu.org/packages/")
|
|
("gnu-elpa-devel" . "https://elpa.gnu.org/devel/")
|
|
("nongnu" . "https://elpa.nongnu.org/nongnu/")
|
|
("melpa" . "https://melpa.org/packages/")))
|
|
|
|
;; Highest number gets priority (what is not mentioned has priority 0)
|
|
(setq package-archive-priorities
|
|
'(("gnu-elpa" . 3)
|
|
("melpa" . 2)
|
|
("nongnu" . 1)))
|
|
|
|
;; Let `package-install' suggest upgrades for built-in packages too.
|
|
(setq package-install-upgrade-built-in t)
|
|
#+end_src
|
|
|
|
** The =init.el= macro to do nothing with Elisp code (~prot-emacs-comment~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:3b14faa6-83fd-4d5f-b3bc-85f72fd572d4
|
|
:END:
|
|
|
|
#+begin_quote
|
|
This is something I learnt while studying Clojure: a ~comment~ macro
|
|
that wraps some code, effectively commenting it out, while keeping
|
|
indentation and syntax highlighting intact.
|
|
|
|
What I have here is technically not commenting out the code, because
|
|
the expansion of the macro is nil, not the actual code with comments
|
|
around it.
|
|
#+end_quote
|
|
|
|
#+begin_example emacs-lisp
|
|
(defmacro prot-emacs-comment (&rest body)
|
|
"Do nothing with BODY and return nil, with no side effects."
|
|
(declare (indent defun))
|
|
nil)
|
|
#+end_example
|
|
|
|
#+begin_quote
|
|
The above is an example. What I actually use is the following. It
|
|
behaves the same as above, except when it reads a plist of the form
|
|
=(:eval t)=. The idea is for me to quickly activate something I want
|
|
to test by passing that to the macro. So here we have it:
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
(defmacro prot-emacs-comment (&rest body)
|
|
"Determine what to do with BODY.
|
|
|
|
If BODY contains an unquoted plist of the form (:eval t) then
|
|
return BODY inside a `progn'.
|
|
|
|
Otherwise, do nothing with BODY and return nil, with no side
|
|
effects."
|
|
(declare (indent defun))
|
|
(let ((eval))
|
|
(dolist (element body)
|
|
(when-let* (((plistp element))
|
|
(key (car element))
|
|
((eq key :eval))
|
|
(val (cadr element)))
|
|
(setq eval val
|
|
body (delq element body))))
|
|
(when eval `(progn ,@body))))
|
|
#+end_src
|
|
|
|
** The =init.el= macro to define abbreviations (~prot-emacs-abbrev~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:e7a12825-7848-42bd-b99b-b87903012814
|
|
:END:
|
|
|
|
[ Watch Prot's video: [[https://protesilaos.com/codelog/2024-02-03-emacs-abbrev-mode/][abbreviations with abbrev-mode (quick text expansion)]] (2024-02-03). ]
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
(defmacro prot-emacs-abbrev (table &rest definitions)
|
|
"Expand abbrev DEFINITIONS for the given TABLE.
|
|
DEFINITIONS is a sequence of (i) string pairs mapping the
|
|
abbreviation to its expansion or (ii) a string and symbol pair
|
|
making an abbreviation to a function."
|
|
(declare (indent 1))
|
|
(unless (zerop (% (length definitions) 2))
|
|
(error "Uneven number of key+command pairs"))
|
|
`(if (abbrev-table-p ,table)
|
|
(progn
|
|
,@(mapcar
|
|
(lambda (pair)
|
|
(let ((abbrev (nth 0 pair))
|
|
(expansion (nth 1 pair)))
|
|
(if (stringp expansion)
|
|
`(define-abbrev ,table ,abbrev ,expansion)
|
|
`(define-abbrev ,table ,abbrev "" ,expansion))))
|
|
(seq-split definitions 2)))
|
|
(error "%s is not an abbrev table" ,table)))
|
|
#+end_src
|
|
|
|
** The =init.el= final part to load the individual modules
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:e6c4acf5-5b51-4b38-a86a-bf3f698ac872
|
|
:END:
|
|
|
|
Now we are ready to load our per-module configuration files:
|
|
|
|
#+begin_src emacs-lisp :tangle "init.el"
|
|
(require 'unravel-theme)
|
|
(require 'unravel-essentials)
|
|
;; (require 'unravel-modeline)
|
|
(require 'unravel-completion)
|
|
;; (require 'unravel-search)
|
|
;; (require 'unravel-dired)
|
|
;; (require 'unravel-window)
|
|
;; (require 'unravel-git)
|
|
;; (require 'unravel-org)
|
|
(require 'unravel-langs)
|
|
#+end_src
|
|
|
|
* The =unravel-theme.el= module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:8cf67c82-1ebb-4be8-b0e7-161bbf5419ce
|
|
:END:
|
|
|
|
This module defines everything related to the aesthetics of Emacs.
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el" :mkdirp yes
|
|
;;; Everything related to the look of Emacs
|
|
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section for cool, modern themes (~ef-themes~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:2b2a27a1-6d2e-4b59-bf60-94682e173f2f
|
|
:END:
|
|
|
|
I use themes from the ~ef-themes~ package exclusively.
|
|
|
|
Prot is the lead developer and maintainer of this package.
|
|
|
|
+ Package name (GNU ELPA): ~ef-themes~
|
|
+ Official manual: <https://protesilaos.com/emacs/ef-themes>
|
|
+ Change log: <https://protesilaos.com/emacs/ef-themes-changelog>
|
|
+ Git repositories:
|
|
- GitHub: <https://github.com/protesilaos/ef-themes>
|
|
- GitLab: <https://gitlab.com/protesilaos/ef-themes>
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;; The Ef (εὖ) themes
|
|
|
|
;; The themes are customisable. Read the manual:
|
|
;; <https://protesilaos.com/emacs/ef-themes>.
|
|
(use-package ef-themes
|
|
:ensure t
|
|
:demand t
|
|
:bind
|
|
(("<f5>" . ef-themes-rotate)
|
|
("C-<f5>" . ef-themes-select))
|
|
:config
|
|
(setq ef-themes-to-toggle '(ef-elea-light ef-elea-dark)
|
|
ef-themes-variable-pitch-ui t
|
|
ef-themes-mixed-fonts t
|
|
ef-themes-headings ; read the manual's entry of the doc string
|
|
'((0 . (variable-pitch light 1.9))
|
|
(1 . (variable-pitch light 1.8))
|
|
(2 . (variable-pitch regular 1.7))
|
|
(3 . (variable-pitch regular 1.6))
|
|
(4 . (variable-pitch regular 1.5))
|
|
(5 . (variable-pitch 1.4)) ; absence of weight means `bold'
|
|
(6 . (variable-pitch 1.3))
|
|
(7 . (variable-pitch 1.2))
|
|
(agenda-date . (semilight 1.5))
|
|
(agenda-structure . (variable-pitch light 1.9))
|
|
(t . (variable-pitch 1.1))))
|
|
|
|
(ef-themes-select 'ef-elea-light))
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section for ~lin~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:bf5b4d08-8f33-4a8c-8ecd-fca19bf2497a
|
|
:END:
|
|
|
|
~lin~ is an improvement on ~hl-line-mode~.
|
|
|
|
Prot is the lead developer and maintainer of this package.
|
|
|
|
+ Package name (GNU ELPA): ~lin~
|
|
+ Official manual: <https://protesilaos.com/emacs/lin>
|
|
+ Change log: <https://protesilaos.com/emacs/lin-changelog>
|
|
+ Git repositories:
|
|
- GitHub: <https://github.com/protesilaos/lin>
|
|
- GitLab: <https://gitlab.com/protesilaos/lin>
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;;; Lin
|
|
;; Read the lin manual: <https://protesilaos.com/emacs/lin>.
|
|
(use-package lin
|
|
:ensure t
|
|
:hook (after-init . lin-global-mode) ; applies to all `lin-mode-hooks'
|
|
:config
|
|
(setopt lin-face 'lin-cyan))
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section for ~spacious-padding~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:6c118185-fcb1-4c9a-93af-71814cb84279
|
|
:END:
|
|
|
|
~spacious-padding~ gives us a comfortable reading experience.
|
|
|
|
Prot is the lead developer and maintainer of this package.
|
|
|
|
#+begin_quote
|
|
Inspiration for this package comes from [[https://github.com/rougier][Nicolas Rougier's impressive
|
|
designs]] and [[https://github.com/minad/org-modern][Daniel Mendler's ~org-modern~ package]].
|
|
#+end_quote
|
|
|
|
+ Package name (GNU ELPA): ~spacious-padding~
|
|
+ Official manual: <https://protesilaos.com/emacs/spacious-padding>
|
|
+ Git repositories:
|
|
- GitHub: <https://github.com/protesilaos/spacious-padding>
|
|
- GitLab: <https://gitlab.com/protesilaos/spacious-padding>
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;;; Increase padding of windows/frames
|
|
;; <https://protesilaos.com/codelog/2023-06-03-emacs-spacious-padding/>.
|
|
(use-package spacious-padding
|
|
:ensure t
|
|
:if (display-graphic-p)
|
|
:hook (after-init . spacious-padding-mode)
|
|
:init
|
|
;; These are the defaults, but I keep it here for visiibility.
|
|
(setq spacious-padding-widths
|
|
'(:internal-border-width 30
|
|
:header-line-width 4
|
|
:mode-line-width 6
|
|
:tab-width 4
|
|
:right-divider-width 30
|
|
:scroll-bar-width 8
|
|
:left-fringe-width 20
|
|
:right-fringe-width 20))
|
|
|
|
;; Read the doc string of `spacious-padding-subtle-mode-line' as
|
|
;; it is very flexible.
|
|
(setq spacious-padding-subtle-mode-line t))
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section for ~rainbow-mode~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:9438236e-a8a4-45e0-8c61-8268c634d50b
|
|
:END:
|
|
|
|
#+begin_quote
|
|
This package produces an in-buffer preview of a colour value. I use
|
|
those while developing my themes, hence the ~prot/rainbow-mode-in-themes~
|
|
to activate ~rainbow-mode~ if I am editing a theme file.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;;; Rainbow mode for colour previewing (rainbow-mode.el)
|
|
(use-package rainbow-mode
|
|
:ensure t
|
|
:init
|
|
(setq rainbow-ansi-colors nil)
|
|
(setq rainbow-x-colors nil)
|
|
|
|
(defun prot/rainbow-mode-in-themes ()
|
|
(when-let* ((file (buffer-file-name))
|
|
((derived-mode-p 'emacs-lisp-mode))
|
|
((string-match-p "-theme" file)))
|
|
(rainbow-mode 1)))
|
|
:bind ( :map ctl-x-x-map
|
|
("c" . rainbow-mode)) ; C-x x c
|
|
:hook (emacs-lisp-mode . prot/rainbow-mode-in-themes))
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section for ~cursory~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:34ce98fe-0b57-44d9-b5f3-0224632114a5
|
|
:END:
|
|
|
|
#+begin_quote
|
|
My ~cursory~ package provides a thin wrapper around built-in variables
|
|
that affect the style of the Emacs cursor on graphical terminals. The
|
|
intent is to allow the user to define preset configurations such as
|
|
"block with slow blinking" or "bar with fast blinking" and set them on
|
|
demand. The use-case for such presets is to adapt to evolving
|
|
interface requirements and concomitant levels of expected comfort,
|
|
such as in the difference between writing and reading.
|
|
#+end_quote
|
|
|
|
Prot is the lead developer and maintainer.
|
|
|
|
+ Package name (GNU ELPA): ~cursory~
|
|
+ Official manual: <https://protesilaos.com/emacs/cursory>
|
|
+ Change log: <https://protesilaos.com/emacs/cursory-changelog>
|
|
+ Git repositories:
|
|
- GitHub: <https://github.com/protesilaos/cursory>
|
|
- GitLab: <https://gitlab.com/protesilaos/cursory>
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;; Cursor appearance (cursory)
|
|
;; Read the manual: <https://protesilaos.com/emacs/cursory>.
|
|
(use-package cursory
|
|
:ensure t
|
|
:demand t
|
|
:if (display-graphic-p)
|
|
:config
|
|
(setq cursory-presets
|
|
'((box
|
|
:blink-cursor-interval 1.2)
|
|
(box-no-blink
|
|
:blink-cursor-mode -1)
|
|
(bar
|
|
:cursor-type (bar . 2)
|
|
:blink-cursor-interval 0.8)
|
|
(bar-no-other-window
|
|
:inherit bar
|
|
:cursor-in-non-selected-windows nil)
|
|
(bar-no-blink
|
|
:cursor-type (bar . 2)
|
|
:blink-cursor-mode -1)
|
|
(underscore
|
|
:cursor-type (hbar . 3)
|
|
:blink-cursor-interval 0.3
|
|
:blink-cursor-blinks 50)
|
|
(underscore-no-other-window
|
|
:inherit underscore
|
|
:cursor-in-non-selected-windows nil)
|
|
(underscore-thick
|
|
:cursor-type (hbar . 8)
|
|
:blink-cursor-interval 0.3
|
|
:blink-cursor-blinks 50
|
|
:cursor-in-non-selected-windows (hbar . 3))
|
|
(underscore-thick-no-blink
|
|
:blink-cursor-mode -1
|
|
:cursor-type (hbar . 8)
|
|
:cursor-in-non-selected-windows (hbar . 3))
|
|
(t ; the default values
|
|
:cursor-type box
|
|
:cursor-in-non-selected-windows hollow
|
|
:blink-cursor-mode 1
|
|
:blink-cursor-blinks 10
|
|
:blink-cursor-interval 0.2
|
|
:blink-cursor-delay 0.2)))
|
|
|
|
;; I am using the default values of `cursory-latest-state-file'.
|
|
|
|
;; Set last preset or fall back to desired style from `cursory-presets'.
|
|
(cursory-set-preset (or (cursory-restore-latest-preset) 'box))
|
|
|
|
(cursory-mode 1))
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section for ~theme-buffet~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:2af10314-c8c2-4946-bf9c-a5b0f5fe881b
|
|
:END:
|
|
|
|
The ~theme-buffet~ package automatically changes the theme based on time of day.
|
|
|
|
Bruno Boal is the lead developer and Prot is a co-maintainer.
|
|
|
|
+ Package name (GNU ELPA): ~theme-buffet~
|
|
+ Git repo on SourceHut: <https://git.sr.ht/~bboal/theme-buffet>
|
|
- Mirrors:
|
|
+ GitHub: <https://github.com/BBoal/theme-buffet>
|
|
+ Codeberg: <https://codeberg.org/BBoal/theme-buffet>
|
|
+ Mailing list: <https://lists.sr.ht/~bboal/general-issues>
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;;; Theme buffet
|
|
;; <https://git.sr.ht/~bboal/theme-buffet>
|
|
(use-package theme-buffet
|
|
:ensure t
|
|
:after (:any modus-themes ef-themes)
|
|
:defer 1
|
|
:config
|
|
(let ((modus-themes-p (featurep 'modus-themes))
|
|
(ef-themes-p (featurep 'ef-themes)))
|
|
(setq theme-buffet-menu 'end-user)
|
|
(setq theme-buffet-time-offset 0)
|
|
(setq theme-buffet-end-user
|
|
'(:night (ef-dark ef-winter ef-autumn ef-night ef-duo-dark ef-symbiosis ef-owl)
|
|
:morning (ef-light ef-cyprus ef-spring ef-frost ef-duo-light ef-eagle)
|
|
:afternoon (ef-arbutus ef-day ef-kassio ef-summer ef-elea-light ef-maris-light ef-melissa-light ef-trio-light ef-reverie)
|
|
:evening (ef-rosa ef-elea-dark ef-maris-dark ef-melissa-dark ef-trio-dark ef-dream)))
|
|
|
|
(when (or modus-themes-p ef-themes-p)
|
|
(theme-buffet-timer-hours 2))))
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section about ~fontaine~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:cb41fef0-41a5-4a85-9552-496d96290258
|
|
:END:
|
|
|
|
[ Watch Prot's video: [[https://protesilaos.com/codelog/2024-01-16-customize-emacs-fonts/][Customise Emacs fonts]] (2024-01-16) ]
|
|
|
|
#+begin_quote
|
|
My ~fontaine~ package allows the user to define detailed font
|
|
configurations and set them on demand. For example, one can have a
|
|
=regular-editing= preset and another for =presentation-mode= (these
|
|
are arbitrary, user-defined symbols): the former uses small fonts
|
|
which are optimised for writing, while the latter applies typefaces
|
|
that are pleasant to read at comfortable point sizes.
|
|
#+end_quote
|
|
|
|
Prot is the lead developer and maintainer.
|
|
|
|
+ Package name (GNU ELPA): ~fontaine~
|
|
+ Official manual: <https://protesilaos.com/emacs/fontaine>
|
|
+ Change log: <https://protesilaos.com/emacs/fontaine-changelog>
|
|
+ Git repositories:
|
|
- GitHub: <https://github.com/protesilaos/fontaine>
|
|
- GitLab: <https://gitlab.com/protesilaos/fontaine>
|
|
|
|
Another section defines some complementary functionality
|
|
([[#h:60d6aae2-6e4b-402c-b6a8-411fc49a6857][The =unravel-theme.el= section about ~variable-pitch-mode~ and font resizing]]).
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;;; Fontaine (font configurations)
|
|
;; Read the manual: <https://protesilaos.com/emacs/fontaine>
|
|
(use-package fontaine
|
|
:ensure t
|
|
:if (display-graphic-p)
|
|
:hook
|
|
;; Persist the latest font preset when closing/starting Emacs and
|
|
;; while switching between themes.
|
|
((after-init . fontaine-mode)
|
|
(after-init . (lambda ()
|
|
;; Set last preset or fall back to desired style from `fontaine-presets'.
|
|
(fontaine-set-preset (or (fontaine-restore-latest-preset) 'regular)))))
|
|
:config
|
|
;; This is defined in Emacs C code: it belongs to font settings.
|
|
(setq x-underline-at-descent-line nil)
|
|
|
|
;; And this is for Emacs28.
|
|
(setq-default text-scale-remap-header-line t)
|
|
|
|
;; This is the default value. Just including it here for
|
|
;; completeness.
|
|
(setq fontaine-latest-state-file (locate-user-emacs-file "fontaine-latest-state.eld"))
|
|
|
|
(setq fontaine-presets
|
|
'((small
|
|
:default-height 80)
|
|
(regular) ; like this it uses all the fallback values and is named `regular'
|
|
(medium
|
|
:default-weight semilight
|
|
:default-height 115
|
|
:bold-weight extrabold)
|
|
(large
|
|
:inherit medium
|
|
:default-height 150)
|
|
(live-stream
|
|
:default-family "Iosevka"
|
|
:default-height 150
|
|
:default-weight medium
|
|
:fixed-pitch-family "Iosevka"
|
|
:variable-pitch-family "Iosevka"
|
|
:bold-weight extrabold)
|
|
(presentation
|
|
:default-height 180)
|
|
(jumbo
|
|
:default-height 260)
|
|
(t
|
|
;; I keep all properties for didactic purposes, but most can be
|
|
;; omitted. See the fontaine manual for the technicalities:
|
|
;; <https://protesilaos.com/emacs/fontaine>.
|
|
:default-family "Iosevka"
|
|
:default-weight regular
|
|
:default-slant normal
|
|
:default-width normal
|
|
:default-height 100
|
|
|
|
:fixed-pitch-family "Iosevka Fixed"
|
|
:fixed-pitch-weight nil
|
|
:fixed-pitch-slant nil
|
|
:fixed-pitch-width nil
|
|
:fixed-pitch-height 1.0
|
|
|
|
:fixed-pitch-serif-family nil
|
|
:fixed-pitch-serif-weight nil
|
|
:fixed-pitch-serif-slant nil
|
|
:fixed-pitch-serif-width nil
|
|
:fixed-pitch-serif-height 1.0
|
|
|
|
:variable-pitch-family "Iosevka"
|
|
:variable-pitch-weight nil
|
|
:variable-pitch-slant nil
|
|
:variable-pitch-width nil
|
|
:variable-pitch-height 1.0
|
|
|
|
:mode-line-active-family "Iosevka Term"
|
|
:mode-line-active-weight nil
|
|
:mode-line-active-slant nil
|
|
:mode-line-active-width nil
|
|
:mode-line-active-height 1.0
|
|
|
|
:mode-line-inactive-family "Iosevka Term"
|
|
:mode-line-inactive-weight nil
|
|
:mode-line-inactive-slant nil
|
|
:mode-line-inactive-width nil
|
|
:mode-line-inactive-height 1.0
|
|
|
|
:header-line-family "Iosevka Term"
|
|
:header-line-weight nil
|
|
:header-line-slant nil
|
|
:header-line-width nil
|
|
:header-line-height 1.0
|
|
|
|
:line-number-family "Iosevka Term"
|
|
:line-number-weight nil
|
|
:line-number-slant nil
|
|
:line-number-width nil
|
|
:line-number-height 1.0
|
|
|
|
:tab-bar-family "Iosevka Term"
|
|
:tab-bar-weight nil
|
|
:tab-bar-slant nil
|
|
:tab-bar-width nil
|
|
:tab-bar-height 1.0
|
|
|
|
:tab-line-family "Iosevka Term"
|
|
:tab-line-weight nil
|
|
:tab-line-slant nil
|
|
:tab-line-width nil
|
|
:tab-line-height 1.0
|
|
|
|
:bold-family "Iosevka"
|
|
:bold-slant nil
|
|
:bold-weight bold
|
|
:bold-width nil
|
|
:bold-height 1.0
|
|
|
|
:italic-family "Iosevka"
|
|
:italic-weight nil
|
|
:italic-slant italic
|
|
:italic-width nil
|
|
:italic-height 1.0
|
|
|
|
:line-spacing nil))))
|
|
#+end_src
|
|
|
|
** The =unravel-theme.el= section about ~variable-pitch-mode~ and font resizing
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:60d6aae2-6e4b-402c-b6a8-411fc49a6857
|
|
:END:
|
|
|
|
[ Watch Prot's video: [[https://protesilaos.com/codelog/2024-01-16-customize-emacs-fonts/][Customise Emacs fonts]] (2024-01-16) ]
|
|
|
|
#+begin_quote
|
|
The built-in ~variable-pitch-mode~ makes the current buffer use a
|
|
proportionately spaced font. In technical terms, it remaps the
|
|
~default~ face to ~variable-pitch~, so whatever applies to the latter
|
|
takes effect over the former. I take care of their respective font
|
|
families in my ~fontaine~ setup ([[#h:cb41fef0-41a5-4a85-9552-496d96290258][The =prot-emacs-theme.el= section about ~fontaine~]]).
|
|
|
|
I want to activate ~variable-pitch-mode~ in all buffers where I
|
|
normally focus on prose. The exact mode hooks are specified in the
|
|
variable =prot/enable-variable-pitch-in-hooks=. Exceptions to these
|
|
are major modes that I do not consider related to prose (and which in
|
|
my opinion should not be derived from ~text-mode~): these are excluded
|
|
in the function ~prot/enable-variable-pitch~.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
;;;;; `variable-pitch-mode' setup
|
|
(use-package face-remap
|
|
:ensure nil
|
|
:functions prot/enable-variable-pitch
|
|
:bind ( :map ctl-x-x-map
|
|
("v" . variable-pitch-mode))
|
|
:hook ((text-mode notmuch-show-mode elfeed-show-mode) . prot/enable-variable-pitch)
|
|
:config
|
|
;; NOTE 2022-11-20: This may not cover every case, though it works
|
|
;; fine in my workflow. I am still undecided by EWW.
|
|
(defun prot/enable-variable-pitch ()
|
|
(unless (derived-mode-p 'mhtml-mode 'nxml-mode 'yaml-mode)
|
|
(variable-pitch-mode 1)))
|
|
;;;;; Resize keys with global effect
|
|
:bind
|
|
;; Emacs 29 introduces commands that resize the font across all
|
|
;; buffers (including the minibuffer), which is what I want, as
|
|
;; opposed to doing it only in the current buffer. The keys are the
|
|
;; same as the defaults.
|
|
(("C-x C-=" . global-text-scale-adjust)
|
|
("C-x C-+" . global-text-scale-adjust)
|
|
("C-x C-0" . global-text-scale-adjust)))
|
|
#+end_src
|
|
|
|
** Finally, we provide the =unravel-theme.el= module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:bac0ce0a-db68-42e7-ba2c-f350f91f80ef
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-theme.el"
|
|
(provide 'unravel-theme)
|
|
#+end_src
|
|
|
|
* The ~unravel-essentials.el~ module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:0ef52ed9-7b86-4329-ae4e-eff9ab8d07f2
|
|
:END:
|
|
|
|
** The =unravel-essentials.el= block with basic configurations
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:713ede33-3802-40c6-a8e3-7e1fc0d0a924
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el" :mkdirp yes
|
|
;;; Essential configurations
|
|
(use-package emacs
|
|
:ensure nil
|
|
:demand t
|
|
:config
|
|
;;;; General settings and common custom functions
|
|
(setq help-window-select t)
|
|
(setq next-error-recenter '(4)) ; center of the window
|
|
(setq find-library-include-other-files nil) ; Emacs 29
|
|
(setq tramp-connection-timeout (* 60 10)) ; seconds
|
|
(setq save-interprogram-paste-before-kill t)
|
|
(setq mode-require-final-newline t)
|
|
(setq-default truncate-partial-width-windows nil)
|
|
(setq eval-expression-print-length nil)
|
|
(setq kill-do-not-save-duplicates t)
|
|
(setq scroll-error-top-bottom t)
|
|
(setq echo-keystrokes-help t) ; Emacs 30
|
|
(setq epa-keys-select-method 'minibuffer)) ; Emacs 30
|
|
#+end_src
|
|
|
|
** The =unravel-essentials.el= configuration to track recently visited files (~recentf~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:f9aa7523-d88a-4080-add6-073f36cb8b9a
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
(use-package recentf
|
|
:ensure nil
|
|
:hook (after-init . recentf-mode)
|
|
:config
|
|
(setq recentf-max-saved-items 100)
|
|
(setq recentf-max-menu-items 25) ; I don't use the `menu-bar-mode', but this is good to know
|
|
(setq recentf-save-file-modes nil)
|
|
(setq recentf-keep nil)
|
|
(setq recentf-auto-cleanup nil)
|
|
(setq recentf-initialize-file-name-history nil)
|
|
(setq recentf-filename-handlers nil)
|
|
(setq recentf-show-file-shortcuts-flag nil))
|
|
#+end_src
|
|
|
|
** The =unravel-essentials.el= settings for bookmarks
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:581aa0ff-b136-4099-a321-3b86edbfbccb
|
|
:END:
|
|
|
|
#+begin_quote
|
|
Bookmarks are compartments that store arbitrary information about a
|
|
file or buffer. The records are used to recreate that file/buffer
|
|
inside of Emacs. Put differently, we can easily jump back to a file or
|
|
directory (or anything that has a bookmark recorder+handler, really).
|
|
Use the ~bookmark-set~ command (=C-x r m= by default) to record a
|
|
bookmark and then visit one of your bookmarks with ~bookmark-jump~
|
|
(=C-x r b= by default).
|
|
|
|
Also see [[#h:5685df62-4484-42ad-a062-d55ab19022e3][the =unravel-essentials.el= settings for registers]].
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
;;;; Built-in bookmarking framework (bookmark.el)
|
|
(use-package bookmark
|
|
:ensure nil
|
|
:commands (bookmark-set bookmark-jump bookmark-bmenu-list)
|
|
:hook (bookmark-bmenu-mode . hl-line-mode)
|
|
:config
|
|
(setq bookmark-use-annotations nil)
|
|
(setq bookmark-automatically-show-annotations nil)
|
|
(setq bookmark-fringe-mark nil) ; Emacs 29 to hide bookmark fringe icon
|
|
;; Write changes to the bookmark file as soon as 1 modification is
|
|
;; made (addition or deletion). Otherwise Emacs will only save the
|
|
;; bookmarks when it closes, which may never happen properly
|
|
;; (e.g. power failure).
|
|
(setq bookmark-save-flag 1))
|
|
#+end_src
|
|
|
|
** The =unravel-essentials.el= settings for registers
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:5685df62-4484-42ad-a062-d55ab19022e3
|
|
:END:
|
|
|
|
[ Watch Prot's video: [[https://protesilaos.com/codelog/2023-06-28-emacs-mark-register-basics/][Mark and register basics]] (2023-06-28). ]
|
|
|
|
#+begin_quote
|
|
Much like bookmarks, registers store data that we can reinstate
|
|
quickly ([[#h:581aa0ff-b136-4099-a321-3b86edbfbccb][The =unravel-essentials.el= settings for bookmarks]]). A
|
|
common use-case is to write some text to a register and then insert
|
|
that text by calling the given register. This is much better than
|
|
relying on the ~kill-ring~, because registers are meant to be
|
|
overwritten by the user, whereas the ~kill-ring~ accumulates lots of
|
|
text that we do not necessarily need.
|
|
|
|
To me, registers are essential for keyboard macros. By default,
|
|
registers do not persist between Emacs sessions, though I do need to
|
|
re-use them from time to time, hence the arrangement to record them
|
|
with ~savehist-mode~ ([[#h:25765797-27a5-431e-8aa4-cc890a6a913a][The =unravel-completion.el= settings for saving the history (~savehist-mode~)]]).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
;;;; Registers (register.el)
|
|
(use-package register
|
|
:ensure nil
|
|
:defer t ; its commands are autoloaded, so this will be loaded then
|
|
:config
|
|
(setq register-preview-delay 0.8
|
|
register-preview-function #'register-preview-default)
|
|
|
|
(with-eval-after-load 'savehist
|
|
(add-to-list 'savehist-additional-variables 'register-alist)))
|
|
#+end_src
|
|
|
|
** The =unravel-essentials.el= section for ~delete-selection-mode~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:d551b90d-d730-4eb5-976a-24b010fd4db3
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
;;;; Delete selection
|
|
(use-package delsel
|
|
:ensure nil
|
|
:hook (after-init . delete-selection-mode))
|
|
#+end_src
|
|
|
|
** The =unravel-essentials.el= settings for tooltips
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:26afeb95-7920-45ed-8ff6-3648256c280b
|
|
:END:
|
|
|
|
#+begin_quote
|
|
With these settings in place, Emacs will use its own faces and frame
|
|
infrastructure to display tooltips. I prefer it this way because then
|
|
we can benefit from the text properties that can be added to these
|
|
messages (e.g. a different colour or a slant).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
;;;; Tooltips (tooltip-mode)
|
|
(use-package tooltip
|
|
:ensure nil
|
|
:hook (after-init . tooltip-mode)
|
|
:config
|
|
(setq tooltip-delay 0.5
|
|
tooltip-short-delay 0.5
|
|
x-gtk-use-system-tooltips t
|
|
tooltip-frame-parameters
|
|
'((name . "tooltip")
|
|
(internal-border-width . 10)
|
|
(border-width . 0)
|
|
(no-special-glyphs . t))))
|
|
#+end_src
|
|
|
|
** The =unravel-essentials.el= arrangement to run Emacs as a server
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:7709b7e9-844f-49f3-badf-784aacec4bca
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The "server" is functionally like the daemon, except it is run by the
|
|
first Emacs frame we launch. With a running server, we can connect to
|
|
it through a new ~emacsclient~ call. This is useful if we want to
|
|
launch new frames that share resources with the existing running
|
|
process.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
;;;; Emacs server (allow emacsclient to connect to running session)
|
|
(use-package server
|
|
:ensure nil
|
|
:defer 1
|
|
:config
|
|
(setq server-client-instructions nil)
|
|
(unless (server-running-p)
|
|
(server-start)))
|
|
#+end_src
|
|
|
|
** The =prot-emacs-essentials.el= section about ~expreg~ (tree-sitter mark syntactically)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:ceb193bf-0de3-4c43-8ab7-6daa50817754
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~expreg~ package by Yuan Fu (aka casouri) uses the tree-sitter
|
|
framework to incrementally expand the region from the smallest to the
|
|
largest syntactic unit in the given context. This is a powerful
|
|
feature, though it (i) requires Emacs to be built with tree-sitter
|
|
support and (ii) for the user to be running a major mode that is
|
|
designed for tree-sitter (Lisp seems to work regardless).
|
|
|
|
The package offers the ~expreg-expand~ and ~expreg-contract~ commands.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
;;; Mark syntactic constructs efficiently if tree-sitter is available (expreg)
|
|
(when (treesit-available-p)
|
|
(use-package expreg
|
|
:ensure t
|
|
:functions (prot/expreg-expand prot/expreg-expand-dwim)
|
|
:bind ("C-M-SPC" . prot/expreg-expand-dwim) ; overrides `mark-sexp'
|
|
:config
|
|
(defun prot/expreg-expand (n)
|
|
"Expand to N syntactic units, defaulting to 1 if none is provided interactively."
|
|
(interactive "p")
|
|
(dotimes (_ n)
|
|
(expreg-expand)))
|
|
|
|
(defun prot/expreg-expand-dwim ()
|
|
"Do-What-I-Mean `expreg-expand' to start with symbol or word.
|
|
If over a real symbol, mark that directly, else start with a
|
|
word. Fall back to regular `expreg-expand'."
|
|
(interactive)
|
|
(let ((symbol (bounds-of-thing-at-point 'symbol)))
|
|
(cond
|
|
((equal (bounds-of-thing-at-point 'word) symbol)
|
|
(prot/expreg-expand 1))
|
|
(symbol (prot/expreg-expand 2))
|
|
(t (expreg-expand)))))))
|
|
#+end_src
|
|
|
|
** The =unravel-essentials.el= section for Battery display
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:080aa291-95b4-4d54-8783-d156b13190e9
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
;;;; Show battery status on the mode line (battery.el)
|
|
(use-package battery
|
|
:ensure nil
|
|
:hook (after-init . display-battery-mode)
|
|
:config
|
|
(setq battery-mode-line-format
|
|
(cond
|
|
((eq battery-status-function #'battery-linux-proc-acpi)
|
|
"⏻ %b%p%%,%d°C ")
|
|
(battery-status-function
|
|
"⏻ %b%p%% "))))
|
|
#+end_src
|
|
|
|
** Finally, we provide the =unravel-essentials.el= module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:c8b2f021-fe5a-4f6b-944c-20340f764fb2
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-essentials.el"
|
|
(provide 'unravel-essentials)
|
|
#+end_src
|
|
|
|
* The ~unravel-completion.el~ module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:15edf2c3-4419-4101-928a-6e224958a741
|
|
:END:
|
|
|
|
** The =unravel-completion.el= settings for completion styles
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:14b09958-279e-4069-81e3-5a16c9b69892
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~completion-styles~ are pattern matching algorithms. They
|
|
interpret user input and match candidates accordingly.
|
|
|
|
- emacs22 :: Prefix completion that only operates on the text before
|
|
point. If we are in =prefix|suffix=, with =|= representing the
|
|
cursor, it will consider everything that expands =prefix= and then
|
|
add back to it the =suffix=.
|
|
|
|
- basic :: Prefix completion that also accounts for the text after
|
|
point. Using the above example, this one will consider patterns that
|
|
match all of ~emacs22~ as well as anything that completes =suffix=.
|
|
|
|
- partial-completion :: This is used for file navigation. Instead of
|
|
typing out a full path like =~/.local/share/fonts=, we do =~/.l/s/f=
|
|
or variants thereof to make the matches unique such as =~/.l/sh/fon=.
|
|
It is a joy to navigate the file system in this way.
|
|
|
|
- substring :: Matches the given sequence of characters literally
|
|
regardless of where it is in a word. So =pro= will match
|
|
=professional= as well as =reproduce=.
|
|
|
|
- flex :: Completion of an in-order subset of characters. It does not
|
|
matter where the charactes are in the word, so long as they are
|
|
encountered in the given order. The input =lad= will thus match
|
|
~list-faces-display~ as well as ~pulsar-highlight-dwim~.
|
|
|
|
- initials :: Completion of acronyms and initialisms. Typing =lfd=
|
|
will thus match ~list-faces-display~. This completion style can also
|
|
be used for file system navigation, though I prefer to only have
|
|
~partial-completion~ handle that task.
|
|
|
|
- orderless :: This is the only completion style I use which is not
|
|
built into Emacs and which I tweak further in a separate section
|
|
([[#h:7cc77fd0-8f98-4fc0-80be-48a758fcb6e2][The =prot-emacs-completion.el= for the ~orderless~ completion style]]).
|
|
It matches patterns out-of-order. Patterns are typically words
|
|
separated by spaces, though they can also be regular expressions,
|
|
and even styles that are the same as the aforementioned ~flex~ and
|
|
~initials~.
|
|
|
|
Now that you know about the completion styles I use, take a look at
|
|
the value of my ~completion-styles~. You will notice that ~orderless~,
|
|
which is the most powerful/flexible is placed last. I do this because
|
|
Emacs tries the styles in the given order from left to right, moving
|
|
the next one until it finds a match. As such, I usually want to start
|
|
with tight matches (e.g. =li-fa-di= for ~list-faces-display~) and only
|
|
widen the scope of the search as I need to. This is easy to do because
|
|
none of the built-in completion styles parses the empty space, so as
|
|
soon as I type a space after some characters I am using ~orderless~.
|
|
#+end_quote
|
|
|
|
(There are more details in Prot's file, for the interested reader)
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el" :mkdirp yes
|
|
;;; General minibuffer settings
|
|
(use-package minibuffer
|
|
:ensure nil
|
|
:config
|
|
;;;; Completion styles
|
|
(setq completion-styles '(basic substring initials flex orderless)) ; also see `completion-category-overrides'
|
|
(setq completion-pcm-leading-wildcard t) ; Emacs 31: make `partial-completion' behave like `substring'
|
|
|
|
;; Reset all the per-category defaults so that (i) we use the
|
|
;; standard `completion-styles' and (ii) can specify our own styles
|
|
;; in the `completion-category-overrides' without having to
|
|
;; explicitly override everything.
|
|
(setq completion-category-defaults nil)
|
|
|
|
;; A non-exhaustve list of known completion categories:
|
|
;;
|
|
;; - `bookmark'
|
|
;; - `buffer'
|
|
;; - `charset'
|
|
;; - `coding-system'
|
|
;; - `color'
|
|
;; - `command' (e.g. `M-x')
|
|
;; - `customize-group'
|
|
;; - `environment-variable'
|
|
;; - `expression'
|
|
;; - `face'
|
|
;; - `file'
|
|
;; - `function' (the `describe-function' command bound to `C-h f')
|
|
;; - `info-menu'
|
|
;; - `imenu'
|
|
;; - `input-method'
|
|
;; - `kill-ring'
|
|
;; - `library'
|
|
;; - `minor-mode'
|
|
;; - `multi-category'
|
|
;; - `package'
|
|
;; - `project-file'
|
|
;; - `symbol' (the `describe-symbol' command bound to `C-h o')
|
|
;; - `theme'
|
|
;; - `unicode-name' (the `insert-char' command bound to `C-x 8 RET')
|
|
;; - `variable' (the `describe-variable' command bound to `C-h v')
|
|
;; - `consult-grep'
|
|
;; - `consult-isearch'
|
|
;; - `consult-kmacro'
|
|
;; - `consult-location'
|
|
;; - `embark-keybinding'
|
|
;;
|
|
(setq completion-category-overrides
|
|
;; NOTE 2021-10-25: I am adding `basic' because it works better as a
|
|
;; default for some contexts. Read:
|
|
;; <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=50387>.
|
|
;;
|
|
;; `partial-completion' is a killer app for files, because it
|
|
;; can expand ~/.l/s/fo to ~/.local/share/fonts.
|
|
;;
|
|
;; If `basic' cannot match my current input, Emacs tries the
|
|
;; next completion style in the given order. In other words,
|
|
;; `orderless' kicks in as soon as I input a space or one of its
|
|
;; style dispatcher characters.
|
|
'((file (styles . (basic partial-completion orderless)))
|
|
(bookmark (styles . (basic substring)))
|
|
(library (styles . (basic substring)))
|
|
(embark-keybinding (styles . (basic substring)))
|
|
(imenu (styles . (basic substring orderless)))
|
|
(consult-location (styles . (basic substring orderless)))
|
|
(kill-ring (styles . (emacs22 orderless)))
|
|
(eglot (styles . (emacs22 substring orderless))))))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= for the ~orderless~ completion style
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:7cc77fd0-8f98-4fc0-80be-48a758fcb6e2
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~orderless~ package by Omar Antolín Camarena provides one of the
|
|
completion styles that I use ([[#h:14b09958-279e-4069-81e3-5a16c9b69892][The =prot-emacs-completion.el= settings for completion styles]]).
|
|
It is a powerful pattern matching algorithm that parses user input and
|
|
interprets it out-of-order, so that =in pa= will cover ~insert-pair~
|
|
as well as ~package-install~. Components of the search are
|
|
space-separated, by default, though we can modify the user option
|
|
~orderless-component-separator~ to have something else (but I cannot
|
|
think of a better value). In the section about completion styles, I
|
|
explain how I use ~orderless~ and why its power does not result in
|
|
lots of false positives.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
;;; Orderless completion style
|
|
(use-package orderless
|
|
:ensure t
|
|
:demand t
|
|
:after minibuffer
|
|
:config
|
|
;; Remember to check my `completion-styles' and the
|
|
;; `completion-category-overrides'.
|
|
(setq orderless-matching-styles '(orderless-prefixes orderless-regexp))
|
|
|
|
;; SPC should never complete: use it for `orderless' groups.
|
|
;; The `?' is a regexp construct.
|
|
:bind ( :map minibuffer-local-completion-map
|
|
("SPC" . nil)
|
|
("?" . nil)))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= settings to ignore letter casing
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:7fe1787d-dba3-46fe-82a9-5dc5f8ea6217
|
|
:END:
|
|
|
|
#+begin_quote
|
|
I never really need to match letters case-sensitively in the
|
|
minibuffer. Let's have everything ignore casing by default.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
(setq completion-ignore-case t)
|
|
(setq read-buffer-completion-ignore-case t)
|
|
(setq-default case-fold-search t) ; For general regexp
|
|
(setq read-file-name-completion-ignore-case t)
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= settings for common interactions
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:b640f032-ad11-413e-ad8f-63408671d500
|
|
:END:
|
|
|
|
#+begin_quote
|
|
Here I combine several small tweaks to improve the overall minibuffer
|
|
experience.
|
|
|
|
- The need to ~resize-mini-windows~ arises on some occasions where
|
|
Emacs has to show text spanning multiple lines in the "mini
|
|
windows". A common scenario for me is in Org mode buffers where I
|
|
set the =TODO= keyword of a task with =C-c C-t= (=M-x org-todo=) and
|
|
have this as my setting: ~(setq org-use-fast-todo-selection 'expert)~
|
|
Otherwise, this is not an issue anyway and I may also like other
|
|
options for ~org-use-fast-todo-selection~.
|
|
|
|
- The ~read-answer-short~ is complementary to ~use-short-answers~.
|
|
This is about providing the shorter version to some confirmation
|
|
prompt, such as =y= instead of =yes=.
|
|
|
|
- The ~echo-keystrokes~ is set to a low value to show in the echo area
|
|
the incomplete key sequence I have just typed. This is especially
|
|
helpful for demonstration purposes but also to double check that I
|
|
did not mistype something (I cannot touch-type, so this happens a lot).
|
|
|
|
- The ~minibuffer-prompt-properties~ and advice to ~completing-read-multiple~
|
|
make it so that (i) the minibuffer prompt is not accessible with
|
|
regular motions to avoid mistakes and (ii) prompts that complete
|
|
multiple targets show an indicator about this fact. With regard to
|
|
the latter in particular, we have prompts like that of Org to set
|
|
tags for a heading (with =C-c C-q= else =M-x org-set-tags-command=)
|
|
where more than one candidate can be provided using completion,
|
|
provided each candidate is separated by the ~crm-separator~ (a comma
|
|
by default, though Org uses =:= in that scenario).
|
|
|
|
Remember that when using completion in the minibuffer, you can hit
|
|
=TAB= to expand the selected choice without exiting with it. For
|
|
cases when multiple candidates can be selected, you select the
|
|
candidate, =TAB=, then input the ~crm-separator~, and repeat until
|
|
you are done selecting at which point you type =RET=.
|
|
|
|
- Finally the ~file-name-shadow-mode~ is a neat little feature to
|
|
remove the "shadowed" part of a file prompt while using something
|
|
like =C-x C-f= (=M-x find-file=). File name shadowing happens when
|
|
we invoke ~find-file~ and instead of first deleting the contents of
|
|
the minibuffer, we start typing out the file system path we wish to
|
|
visit. For example, I am in =~/Git/Projects/= and type directly
|
|
after it something like =~/.local/share/fonts/=, so Emacs displays
|
|
=~/Git/Projects/~/.local/share/fonts/= with the original part greyed
|
|
out. With ~file-name-shadow-mode~ the "shadowed" part is removed
|
|
altogether. This is especially nice when combined with the
|
|
completion style called ~partial-completion~
|
|
([[#h:14b09958-279e-4069-81e3-5a16c9b69892][The =unravel-completion.el= settings for completion styles]]).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
(use-package rfn-eshadow
|
|
:ensure nil
|
|
:hook (minibuffer-setup . cursor-intangible-mode)
|
|
:config
|
|
;; Not everything here comes from rfn-eshadow.el, but this is fine.
|
|
|
|
(setq resize-mini-windows t)
|
|
(setq read-answer-short t) ; also check `use-short-answers' for Emacs28
|
|
(setq echo-keystrokes 0.25)
|
|
(setq kill-ring-max 60) ; Keep it small
|
|
|
|
;; Do not allow the cursor to move inside the minibuffer prompt. I
|
|
;; got this from the documentation of Daniel Mendler's Vertico
|
|
;; package: <https://github.com/minad/vertico>.
|
|
(setq minibuffer-prompt-properties
|
|
'(read-only t cursor-intangible t face minibuffer-prompt))
|
|
|
|
;; Add prompt indicator to `completing-read-multiple'. We display
|
|
;; [`completing-read-multiple': <separator>], e.g.,
|
|
;; [`completing-read-multiple': ,] if the separator is a comma. This
|
|
;; is adapted from the README of the `vertico' package by Daniel
|
|
;; Mendler. I made some small tweaks to propertize the segments of
|
|
;; the prompt.
|
|
(defun crm-indicator (args)
|
|
(cons (format "[`completing-read-multiple': %s] %s"
|
|
(propertize
|
|
(replace-regexp-in-string
|
|
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
|
|
crm-separator)
|
|
'face 'error)
|
|
(car args))
|
|
(cdr args)))
|
|
|
|
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
|
|
|
|
(file-name-shadow-mode 1))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= generic minibuffer UI settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:de61a607-0bdf-462b-94cd-c0898319590e
|
|
:END:
|
|
|
|
These are some settings for the default completion user interface.
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
(use-package minibuffer
|
|
:ensure nil
|
|
:demand t
|
|
:config
|
|
(setq completions-format 'one-column)
|
|
(setq completion-show-help nil)
|
|
(setq completion-auto-help 'always)
|
|
(setq completion-auto-select nil)
|
|
(setq completions-detailed t)
|
|
(setq completion-show-inline-help nil)
|
|
(setq completions-max-height 6)
|
|
(setq completions-header-format (propertize "%s candidates:\n" 'face 'bold-italic))
|
|
(setq completions-highlight-face 'completions-highlight)
|
|
(setq minibuffer-completion-auto-choose t)
|
|
(setq minibuffer-visible-completions t) ; Emacs 30
|
|
(setq completions-sort 'historical))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= settings for saving the history (~savehist-mode~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:25765797-27a5-431e-8aa4-cc890a6a913a
|
|
:END:
|
|
|
|
#+begin_quote
|
|
Minibuffer prompts can have their own history. When they do not, they
|
|
share a common history of user inputs. Emacs keeps track of that
|
|
history in the current session, but loses it as soon as we close it.
|
|
With ~savehist-mode~ enabled, all minibuffer histories are written to
|
|
a file and are restored when we start Emacs again.
|
|
#+end_quote
|
|
|
|
#+begin_quote
|
|
Since we are already recording minibuffer histories, we can instruct
|
|
~savehist-mode~ to also keep track of additional variables and restore
|
|
them next time we use Emacs. Hence ~savehist-additional-variables~. I
|
|
do this in a few of places:
|
|
|
|
- [[#h:804b858f-7913-47ef-aaf4-8eef5b59ecb4][The =unravel-completion.el= for in-buffer completion popup and preview (~corfu~)]]
|
|
- [[#h:5685df62-4484-42ad-a062-d55ab19022e3][The =unravel-essentials.el= settings for registers]]
|
|
|
|
Note that the user option ~history-length~ applies to each individual
|
|
history variable: it is not about all histories combined.
|
|
|
|
Overall, I am happy with this feature and benefit from it on a daily
|
|
basis.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
;;;; `savehist' (minibuffer and related histories)
|
|
(use-package savehist
|
|
:ensure nil
|
|
:hook (after-init . savehist-mode)
|
|
:config
|
|
(setq savehist-file (locate-user-emacs-file "savehist"))
|
|
(setq history-length 100)
|
|
(setq history-delete-duplicates t)
|
|
(setq savehist-save-minibuffer-history t)
|
|
(add-to-list 'savehist-additional-variables 'kill-ring))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= settings for dynamic text expansion (~dabbrev~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:567bb00f-1d82-4746-93e5-e0f60721728a
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The built-in ~dabbrev~ package provides a text completion method that
|
|
reads the contents of a buffer and expands the text before the cursor
|
|
to match possible candidates. This is done with =M-/= (~dabbrev-expand~)
|
|
which is what I use most of the time to perform in-buffer completions.
|
|
|
|
The term "dabbrev" stands for "dynamic abbreviation". Emacs also has
|
|
static, user-defined abbreviations ([[#h:fd84b79a-351e-40f0-b383-bf520d77834b][Settings for static text expansion
|
|
(~abbrev~)]]).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
(use-package dabbrev
|
|
:ensure nil
|
|
:commands (dabbrev-expand dabbrev-completion)
|
|
:config
|
|
;;;; `dabbrev' (dynamic word completion (dynamic abbreviations))
|
|
(setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
|
|
(setq dabbrev-abbrev-skip-leading-regexp "[$*/=~']")
|
|
(setq dabbrev-backward-only nil)
|
|
(setq dabbrev-case-distinction 'case-replace)
|
|
(setq dabbrev-case-fold-search nil)
|
|
(setq dabbrev-case-replace 'case-replace)
|
|
(setq dabbrev-check-other-buffers t)
|
|
(setq dabbrev-eliminate-newlines t)
|
|
(setq dabbrev-upcase-means-case-search t)
|
|
(setq dabbrev-ignored-buffer-modes
|
|
'(archive-mode image-mode docview-mode pdf-view-mode)))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= for in-buffer completion popup (~corfu~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:804b858f-7913-47ef-aaf4-8eef5b59ecb4
|
|
:END:
|
|
|
|
#+begin_quote
|
|
I generally do not rely on in-buffer text completion. I feel it slows
|
|
me down and distracts me. When I do, however, need to rely on it, I
|
|
have the ~corfu~ package by Daniel Mendler: it handles the task
|
|
splendidly as it works with Emacs' underlying infrastructure for
|
|
~completion-at-point-functions~.
|
|
|
|
Completion is triggered with the =TAB= key, which produces a popup
|
|
where the cursor is. The companion ~corfu-popupinfo-mode~ will show a
|
|
secondary documentation popup if we move over a candidate but do not
|
|
do anything with it.
|
|
|
|
Also see [[#h:567bb00f-1d82-4746-93e5-e0f60721728a][the =prot-emacs-completion.el= settings for dynamic text expansion (~dabbrev~)]].
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
;;; Corfu (in-buffer completion popup)
|
|
(use-package corfu
|
|
:ensure t
|
|
:hook (after-init . global-corfu-mode)
|
|
;; I also have (setq tab-always-indent 'complete) for TAB to complete
|
|
;; when it does not need to perform an indentation change.
|
|
:bind (:map corfu-map ("<tab>" . corfu-complete))
|
|
:config
|
|
(setq corfu-preview-current nil)
|
|
(setq corfu-min-width 20)
|
|
|
|
(setq corfu-popupinfo-delay '(1.25 . 0.5))
|
|
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
|
|
|
|
;; Sort by input history (no need to modify `corfu-sort-function').
|
|
(with-eval-after-load 'savehist
|
|
(corfu-history-mode 1)
|
|
(add-to-list 'savehist-additional-variables 'corfu-history)))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= settings for ~consult~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:22e97b4c-d88d-4deb-9ab3-f80631f9ff1d
|
|
:END:
|
|
|
|
#+begin_quote
|
|
consult~ is another wonderful package by Daniel Mendler. It provides
|
|
a number of commands that turbocharge the minibuffer with advanced
|
|
capabilities for filtering, asynchronous input, and previewing of the
|
|
current candidate's context.
|
|
|
|
- A case where filtering is in use is the ~consult-buffer~ command,
|
|
which many users have as a drop-in replacement to the generic =C-x b=
|
|
(=M-x switch-to-buffer=). It is a one-stop-shop for buffers,
|
|
recently visited files (if ~recentf-mode~ is used---I don't),
|
|
bookmarks ([[#h:581aa0ff-b136-4099-a321-3b86edbfbccb][The =unravel-essentials.el= settings for bookmarks]]),
|
|
and, in principle, anything else that defines a source for this
|
|
interface. To filter those source, we can type at the empty
|
|
minibuffer =b SPC=, which will insert a filter specific to buffers.
|
|
Delete back to remove the =[Buffer]= filter and insert another
|
|
filter. Available filters are displayed by typing =?= at the prompt
|
|
(I define it this way to call the command ~consult-narrow-help~).
|
|
Every multi-source command from ~consult~ relies on this paradigm.
|
|
|
|
- Asynchronous input pertains to the intersection between Emacs and
|
|
external search programs. A case in point is ~consult-grep~, which
|
|
calls the system's ~grep~ program. The prompt distinguishes between
|
|
what is sent to the external program and what is only shown to Emacs
|
|
by wrapping the former inside of =#=. So the input =#prot-#completion=
|
|
will send =prot-= to the ~grep~ program and then use =completion=
|
|
inside of the minibuffer to perform the subsequent pattern-matching
|
|
(e.g. with help from ~orderless~ ([[#h:7cc77fd0-8f98-4fc0-80be-48a758fcb6e2][The =unravel-completion.el= for the ~orderless~ completion style]]).
|
|
The part that is sent to the external program does not block Emacs.
|
|
It is handled asynchronously, so everything stays responsive.
|
|
|
|
- As for previewing, ~consult~ commands show the context of the
|
|
current match and update the window as we move between completion
|
|
candidates in the minibuffer. For example, the ~consult-line~
|
|
command performs an in-buffer search and lets us move between
|
|
matches in the minibuffer while seeing in the window above what the
|
|
surrounding text looks like. This is an excellent feature when we
|
|
are trying to find something and do not quite remember all the
|
|
search terms to narrow down to it simply by typing at the minibuffer
|
|
prompt.
|
|
|
|
Also check: [[#h:e0f9c30e-3a98-4479-b709-7008277749e4][The =unravel-search.el= module]].
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
;;; Enhanced minibuffer commands (consult.el)
|
|
(use-package consult
|
|
:ensure t
|
|
:hook (completion-list-mode . consult-preview-at-point-mode)
|
|
:bind
|
|
(:map global-map
|
|
("M-g M-g" . consult-goto-line)
|
|
("M-K" . consult-keep-lines) ; M-S-k is similar to M-S-5 (M-%)
|
|
("M-F" . consult-focus-lines) ; same principle
|
|
("M-s M-b" . consult-buffer)
|
|
("M-s M-f" . consult-find)
|
|
("M-s M-g" . consult-grep)
|
|
("M-s M-h" . consult-history)
|
|
("M-s M-i" . consult-imenu)
|
|
("M-s M-l" . consult-line)
|
|
("M-s M-m" . consult-mark)
|
|
("M-s M-y" . consult-yank-pop)
|
|
("M-s M-s" . consult-outline)
|
|
:map consult-narrow-map
|
|
("?" . consult-narrow-help))
|
|
:config
|
|
(setq consult-line-numbers-widen t)
|
|
;; (setq completion-in-region-function #'consult-completion-in-region)
|
|
(setq consult-async-min-input 3)
|
|
(setq consult-async-input-debounce 0.5)
|
|
(setq consult-async-input-throttle 0.8)
|
|
(setq consult-narrow-key nil)
|
|
(setq consult-find-args
|
|
(concat "find . -not ( "
|
|
"-path */.git* -prune "
|
|
"-or -path */.cache* -prune )"))
|
|
(setq consult-preview-key 'any)
|
|
(setq consult-project-function nil) ; always work from the current directory (use `cd' to switch directory)
|
|
|
|
(add-to-list 'consult-mode-histories '(vc-git-log-edit-mode . log-edit-comment-ring))
|
|
;; the `imenu' extension is in its own file
|
|
(require 'consult-imenu))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= section about ~embark~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:61863da4-8739-42ae-a30f-6e9d686e1995
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~embark~ package by Omar Antolín Camarena provides a mechanism to
|
|
perform relevant actions in the given context. What constitutes "the
|
|
given context" depends on where the cursor is, such as if it is at the
|
|
end of a symbolic expression in Lisp code or inside the minibuffer.
|
|
The single point of entry is the ~embark-act~ command or variants like
|
|
~embark-dwim~.
|
|
|
|
With ~embark-act~ we gain access to a customisable list of commands
|
|
for the given context. If we are over a Lisp symbol, one possible
|
|
action is to describe it (i.e. produce documentation about it). If we
|
|
are browsing files in the minibuffer, possible actions include file
|
|
operations such as to delete or rename the file. And so on for
|
|
everything.
|
|
|
|
The ~embark-dwim~ command always performs the default action for the
|
|
given context. It is like invoking ~embark-act~ and then typing the
|
|
=RET= key.
|
|
|
|
A killer feature of ~embark~ is the concepts of "collect" and
|
|
"export". These are used in the minibuffer to produce a dedicated
|
|
buffer that contains all the completion candidates. For example, if we
|
|
are reading documentation about =embark-= and have 10 items there, we
|
|
can "collect" the results in their own buffer and then navigate it as
|
|
if it were the minibuffer: =RET= will perform the action that the
|
|
actual minibuffer would have carried out (to show documentation, in
|
|
this case). Similarly, the export mechanism takes the completion
|
|
candidates out of the minibuffer, though it also puts them in a major
|
|
mode that is appropriate for them. Files, for instance, will be placed
|
|
in a Dired buffer ([[#h:f8b08a77-f3a8-42fa-b1a9-f940348889c3][The =unravel-dired.el= module]]).
|
|
|
|
Depending on the configurations about the "indicator", the ~embark-act~
|
|
command will display an informative buffer with keys and their
|
|
corresponding commands.
|
|
|
|
One downside of ~embark~ is that it is hard to know what the context
|
|
is. I have had this experience myself several times, where I though I
|
|
was targeting the URL at point while the actions were about Org source
|
|
blocks, headings, and whatnot. Embark is probably correct in such a
|
|
case, though I cannot make my brain think the way it expects.
|
|
|
|
Another downside, which is also true for ~which-key~,
|
|
is the sheer number of options for each context. I feel that the
|
|
defaults should be more conservative, to have 3-4 actions per context
|
|
to make it easier to find stuff. Those who need more, can add them.
|
|
Documentation can also be provided to that end. Adding commands to
|
|
such a list is not a trivial task, because the user must modify
|
|
keymaps and thus understand the relevant concepts. Sure, we can all
|
|
learn, but this is not your usual ~setq~ tweak.
|
|
|
|
All things considered, I do not recommend ~embark~ to new users as I
|
|
know for a fact that people have trouble using it effectively. Power
|
|
users can benefit from it, though you will notice in the following
|
|
code block and in =prot-embark.el= how even power users need to put in
|
|
some work ([[#h:fb034be5-c316-4c4f-a46f-cebcab332a47][The =prot-embark.el= library]]). Whether it is worth it or
|
|
not depends on one's use-case.
|
|
|
|
Karthik Chikmagalur has an excellently written and presented essay on
|
|
[[https://karthinks.com/software/fifteen-ways-to-use-embark/][Fifteen ways to use Embark]]. If you plan on becoming an ~embark~ power
|
|
user, this will help you.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
;;; Extended minibuffer actions and more (embark.el and prot-embark.el)
|
|
(use-package embark
|
|
:ensure t
|
|
:defer 1
|
|
:config
|
|
(setq embark-confirm-act-all nil)
|
|
(setq embark-mixed-indicator-both nil)
|
|
(setq embark-mixed-indicator-delay 1.0)
|
|
(setq embark-indicators '(embark-mixed-indicator embark-highlight-indicator))
|
|
(setq embark-verbose-indicator-nested nil) ; I think I don't have them, but I do not want them either
|
|
(setq embark-verbose-indicator-buffer-sections '(bindings))
|
|
(setq embark-verbose-indicator-excluded-actions
|
|
'(embark-cycle embark-act-all embark-collect embark-export embark-insert))
|
|
|
|
;; I never cycle and want to disable the damn thing. Normally, a
|
|
;; nil value disables a key binding but here that value is
|
|
;; interpreted as the binding for `embark-act'. So I just add
|
|
;; some obscure key that I do not have. I absolutely do not want
|
|
;; to cycle!
|
|
(setq embark-cycle-key "<XF86Travel>")
|
|
|
|
;; I do not want `embark-org' and am not sure what is loading it.
|
|
;; So I just unsert all the keymaps... This is the nuclear option
|
|
;; but here we are.
|
|
(with-eval-after-load 'embark-org
|
|
(defvar prot/embark-org-keymaps
|
|
'(embark-org-table-cell-map
|
|
embark-org-table-map
|
|
embark-org-link-copy-map
|
|
embark-org-link-map
|
|
embark-org-src-block-map
|
|
embark-org-item-map
|
|
embark-org-plain-list-map
|
|
embark-org-export-in-place-map)
|
|
"List of Embark keymaps for Org.")
|
|
|
|
;; Reset `prot/embark-org-keymaps'.
|
|
(seq-do
|
|
(lambda (keymap)
|
|
(set keymap (make-sparse-keymap)))
|
|
prot/embark-org-keymaps)))
|
|
|
|
;; I define my own keymaps because I only use a few functions in a
|
|
;; limited number of contexts.
|
|
(use-package prot-embark
|
|
:ensure nil
|
|
:after embark
|
|
:bind
|
|
( :map global-map
|
|
("C-," . prot-embark-act-no-quit)
|
|
("C-." . prot-embark-act-quit)
|
|
:map embark-collect-mode-map
|
|
("C-," . prot-embark-act-no-quit)
|
|
("C-." . prot-embark-act-quit)
|
|
:map minibuffer-local-filename-completion-map
|
|
("C-," . prot-embark-act-no-quit)
|
|
("C-." . prot-embark-act-quit))
|
|
:config
|
|
(setq embark-keymap-alist
|
|
'((buffer prot-embark-buffer-map)
|
|
(command prot-embark-command-map)
|
|
(expression prot-embark-expression-map)
|
|
(file prot-embark-file-map)
|
|
(function prot-embark-function-map)
|
|
(identifier prot-embark-identifier-map)
|
|
(package prot-embark-package-map)
|
|
(region prot-embark-region-map)
|
|
(symbol prot-embark-symbol-map)
|
|
(url prot-embark-url-map)
|
|
(variable prot-embark-variable-map)
|
|
(t embark-general-map))))
|
|
|
|
;; Needed for correct exporting while using Embark with Consult
|
|
;; commands.
|
|
(use-package embark-consult
|
|
:ensure t
|
|
:after (embark consult))
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= section to configure completion annotations (~marginalia~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:bd3f7a1d-a53d-4d3e-860e-25c5b35d8e7e
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~marginalia~ package, co-authored by Daniel Mendler and Omar
|
|
Antolín Camarena, provides helpful annotations to the side of
|
|
completion candidates. We see its effect, for example, when we call =M-x=:
|
|
each command has a brief description next to it (taken from its doc
|
|
string) as well as a key binding, if it has one.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
;;; Detailed completion annotations (marginalia.el)
|
|
(use-package marginalia
|
|
:ensure t
|
|
:hook (after-init . marginalia-mode)
|
|
:config
|
|
(setq marginalia-max-relative-age 0)) ; absolute time
|
|
#+end_src
|
|
|
|
** The =unravel-completion.el= section for ~vertico~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:cff33514-d3ac-4c16-a889-ea39d7346dc5
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~vertico~ package by Daniel Mendler displays the minibuffer in a
|
|
vertical layout. Under the hood, it takes care to be responsive and to
|
|
handle even massive completion tables gracefully.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
;;; Vertical completion layout (vertico)
|
|
(use-package vertico
|
|
:ensure t
|
|
:hook (after-init . vertico-mode)
|
|
:config
|
|
(setq vertico-scroll-margin 0)
|
|
(setq vertico-count 5)
|
|
(setq vertico-resize t)
|
|
(setq vertico-cycle t)
|
|
|
|
(with-eval-after-load 'rfn-eshadow
|
|
;; This works with `file-name-shadow-mode' enabled. When you are in
|
|
;; a sub-directory and use, say, `find-file' to go to your home '~/'
|
|
;; or root '/' directory, Vertico will clear the old path to keep
|
|
;; only your current input.
|
|
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)))
|
|
#+end_src
|
|
|
|
** Finally, we provide the ~unravel-completion.el~ module
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-completion.el"
|
|
(provide 'unravel-completion)
|
|
#+end_src
|
|
* The ~unravel-langs.el~ module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:f44afb76-a1d7-4591-934d-b698cc79a792
|
|
:END:
|
|
|
|
** The =unravel-langs.el= settings for TAB
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:559713c8-0e1e-44aa-bca8-0caae01cc8bb
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el" :mkdirp yes
|
|
;;;; Tabs, indentation, and the TAB key
|
|
(use-package emacs
|
|
:ensure nil
|
|
:demand t
|
|
:config
|
|
(setq tab-always-indent 'complete)
|
|
(setq tab-first-completion 'word-or-paren-or-punct) ; Emacs 27
|
|
(setq-default tab-width 4
|
|
indent-tabs-mode nil))
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings ~show-paren-mode~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:7cd21ea6-c5d8-4258-999d-ad94cac2d8bf
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The built-in ~show-paren-mode~ highlights the parenthesis on the
|
|
opposite end of the current symbolic expression. It also highlights
|
|
matching terms of control flow in programming languages that are not
|
|
using parentheses like Lisp: for instance, in a ~bash~ shell script it
|
|
highlights the ~if~ and ~fi~ keywords. This mode also works for prose
|
|
and I use it globally. Simple and effective!
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;;; Parentheses (show-paren-mode)
|
|
(use-package paren
|
|
:ensure nil
|
|
:hook (prog-mode . show-paren-local-mode)
|
|
:config
|
|
(setq show-paren-style 'mixed)
|
|
(setq show-paren-when-point-in-periphery nil)
|
|
(setq show-paren-when-point-inside-paren nil)
|
|
(setq show-paren-context-when-offscreen 'overlay)) ; Emacs 29
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings for ~eldoc~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:a5773a39-a78f-43fa-8feb-669492c1d5a9
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The built-in ~eldoc~ feature is especially useful in programming
|
|
modes. While we are in a function call, it produces an indicator in
|
|
the echo area (where the minibuffer appears upon invocation) that
|
|
shows the name of the function, the arguments it takes, if any, and
|
|
highlights the current argument we are positioned at. This way, we do
|
|
not have to go back to review the signature of the function just to
|
|
remember its arity. Same principle for variables, where ~eldoc-mode~
|
|
puts the first line of their documentation string in the echo area.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;;; Eldoc (Emacs live documentation feedback)
|
|
(use-package eldoc
|
|
:ensure nil
|
|
:hook (prog-mode . eldoc-mode)
|
|
:config
|
|
(setq eldoc-message-function #'message)) ; don't use mode line for M-x eval-expression, etc.
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings for ~eglot~ (LSP client)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:92258aa8-0d8c-4c12-91b4-5f44420435ce
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The built-in ~eglot~ feature, developed and maintained by João Távora,
|
|
is Emacs' own client for the Language Server Protocol (LSP). The LSP
|
|
technology is all about enhancing the ability of a text editor to work
|
|
with a given programming language. This works by installing a
|
|
so-called "language server" on your computer, which the "LSP client"
|
|
(i.e. ~eglot~) will plug into. A typical language server provides the
|
|
following capabilities:
|
|
|
|
- Code completion :: This can be visualised for in-buffer
|
|
automatic expansion of function calls, variables, and the like
|
|
([[#h:804b858f-7913-47ef-aaf4-8eef5b59ecb4][The =unravel-completion.el= for in-buffer completion popup (~corfu~)]]).
|
|
|
|
- Code linting :: To display suggestions, warnings, or errors. These
|
|
are highlighted in the buffer, usually with an underline, and can
|
|
also be displayed in a standalone buffer with the commands
|
|
~flymake-show-buffer-diagnostics~, ~flymake-show-project-diagnostics~
|
|
([[#h:df6d1b52-0306-4ace-9099-17dded11fbed][The =unravel-langs.el= settings for code linting (~flymake~)]]).
|
|
|
|
- Code navigation and cross-referencing :: While over a symbol, use a
|
|
command to jump directly to its definition. The default key bindings
|
|
for going forth and then back are =M-.= (~xref-find-definitions~)
|
|
and =M-,= (~xref-go-back~).
|
|
|
|
...
|
|
|
|
Assuming the language server is installed, to start using the LSP
|
|
client in a given file, do =M-x eglot=. To make this happen
|
|
automatically for every newly visited file, add a hook like this:
|
|
|
|
(add-hook 'SOME-MAJOR-mode #'eglot-ensure)
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;;; Eglot (built-in client for the language server protocol)
|
|
(use-package eglot
|
|
:ensure nil
|
|
:functions (eglot-ensure)
|
|
:commands (eglot)
|
|
:config
|
|
(setq eglot-sync-connect nil)
|
|
(setq eglot-autoshutdown t))
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings for ~markdown-mode~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:c9063898-07ae-4635-8853-bb5f4bbab421
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~markdown-mode~ lets us edit Markdown files. We get syntax
|
|
highlighting and several extras, such as the folding of headings and
|
|
navigation between them. The mode actually provides lots of added
|
|
functionality for GitHub-flavoured Markdown and to preview a Markdown
|
|
file's HTML representation on a web page. Though I only use it for
|
|
basic text editing.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;; Markdown (markdown-mode)
|
|
(use-package markdown-mode
|
|
:ensure t
|
|
:defer t
|
|
:config
|
|
(setq markdown-fontify-code-blocks-natively t))
|
|
#+end_src
|
|
|
|
** The =prot-emacs-langs.el= settings for ~csv-mode~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:bae58479-86c1-410f-867e-c548def65b1c
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The package ~csv-mode~ provides support for =.csv= files. I do need
|
|
this on occasion, even though my use-case is pretty basic. For me, the
|
|
killer feature is the ability to create a virtual tabulated listing
|
|
with the command ~csv-align-mode~: it hides the field delimiter (comma
|
|
or space) and shows a tab stop in its stead.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;; csv-mode
|
|
(use-package csv-mode
|
|
:ensure t
|
|
:commands (csv-align-mode))
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings for spell checking (~flyspell~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:115806c4-88b0-43c1-8db2-d9d8d20a5c17
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;; Flyspell
|
|
(use-package flyspell
|
|
:ensure nil
|
|
:bind
|
|
( :map flyspell-mode-map
|
|
("C-;" . nil)
|
|
:map flyspell-mouse-map
|
|
("<mouse-3>" . flyspell-correct-word))
|
|
:config
|
|
(setq flyspell-issue-message-flag nil)
|
|
(setq flyspell-issue-welcome-flag nil)
|
|
(setq ispell-program-name "aspell")
|
|
(setq ispell-dictionary "en_GB"))
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings for code linting (~flymake~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:df6d1b52-0306-4ace-9099-17dded11fbed
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The built-in ~flymake~ feature defines an interface for viewing the
|
|
output of linter programs. A "linter" parses a file and reports
|
|
possible notes/warnings/errors in it. With ~flymake~ we get these
|
|
diagnostics in the form of a standalone buffer as well as inline
|
|
highlights (typically underlines combined with fringe indicators) for
|
|
the portion of text in question. The linter report is displayed with
|
|
the command ~flymake-show-buffer-diagnostics~, or ~flymake-show-project-diagnostics~.
|
|
Highlights are shown in the context of the file.
|
|
|
|
The built-in ~eglot~ feature uses ~flymake~ internally to handle the
|
|
LSP linter output ([[#h:92258aa8-0d8c-4c12-91b4-5f44420435ce][The =unravel-langs.el= settings for ~eglot~]]).
|
|
|
|
As for what I have in this configuration block, the essentials for me
|
|
are the user options ~flymake-start-on-save-buffer~ and ~flymake-start-on-flymake-mode~
|
|
as they make the linter update its report when the buffer is saved and
|
|
when ~flymake-mode~ is started, respectively. Otherwise, we have to
|
|
run it manually, which is cumbersome.
|
|
|
|
The ~package-lint-flymake~ package by Steve Purcell adds the glue code
|
|
to make ~flymake~ report issues with Emacs Lisp files for the purposes
|
|
of packaging. I use it whenever I work on my numerous Emacs packages.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;; Flymake
|
|
(use-package flymake
|
|
:ensure nil
|
|
:bind
|
|
(:map flymake-mode-map
|
|
("C-c ! s" . flymake-start)
|
|
("C-c ! l" . flymake-show-buffer-diagnostics) ; Emacs28
|
|
("C-c ! L" . flymake-show-project-diagnostics) ; Emacs28
|
|
("C-c ! n" . flymake-goto-next-error)
|
|
("C-c ! p" . flymake-goto-prev-error))
|
|
:config
|
|
(setq flymake-fringe-indicator-position 'left-fringe)
|
|
(setq flymake-suppress-zero-counters t)
|
|
(setq flymake-no-changes-timeout nil)
|
|
(setq flymake-start-on-flymake-mode t)
|
|
(setq flymake-start-on-save-buffer t)
|
|
(setq flymake-proc-compilation-prevents-syntax-check t)
|
|
(setq flymake-wrap-around nil)
|
|
(setq flymake-mode-line-format
|
|
'("" flymake-mode-line-exception flymake-mode-line-counters))
|
|
;; NOTE 2023-07-03: `prot-modeline.el' actually defines the counters
|
|
;; itself and ignores this.
|
|
(setq flymake-mode-line-counter-format
|
|
'("" flymake-mode-line-error-counter
|
|
flymake-mode-line-warning-counter
|
|
flymake-mode-line-note-counter ""))
|
|
(setq flymake-show-diagnostics-at-end-of-line nil)) ; Emacs 30
|
|
|
|
;;; Elisp packaging requirements
|
|
(use-package package-lint-flymake
|
|
:ensure t
|
|
:after flymake
|
|
:config
|
|
(add-hook 'flymake-diagnostic-functions #'package-lint-flymake))
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings for ~outline-minor-mode~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:ffff5f7b-a62b-4d4a-ae29-af75402e5c35
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;; General configurations for prose/writing
|
|
|
|
;;;; `outline' (`outline-mode' and `outline-minor-mode')
|
|
(use-package outline
|
|
:ensure nil
|
|
:bind
|
|
("<f10>" . outline-minor-mode)
|
|
:config
|
|
(setq outline-minor-mode-highlight nil) ; emacs28
|
|
(setq outline-minor-mode-cycle t) ; emacs28
|
|
(setq outline-minor-mode-use-buttons nil) ; emacs29---bless you for the nil option!
|
|
(setq outline-minor-mode-use-margins nil)) ; as above
|
|
#+end_src
|
|
|
|
** The =prot-emacs-langs.el= settings for ~dictionary~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:f91563d8-f176-4555-b45b-ece56de03279
|
|
:END:
|
|
|
|
Use the entry point ~M-x dictionary-search~
|
|
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;;; `dictionary'
|
|
(use-package dictionary
|
|
:ensure nil
|
|
:config
|
|
(setq dictionary-server "dict.org"
|
|
dictionary-default-popup-strategy "lev" ; read doc string
|
|
dictionary-create-buttons nil
|
|
dictionary-use-single-buffer t))
|
|
#+end_src
|
|
|
|
** The =unravel-langs.el= settings for ~denote~ (notes and file-naming)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:e86a66dc-7ef9-4f09-ad7e-946de2034e8d
|
|
:END:
|
|
|
|
#+begin_quote
|
|
This is another one of my packages and is extended by my
|
|
~consult-denote~ package ([[#h:ee82e629-fb05-4c75-9175-48a760a25691][The =unravel-langs.el= integration between Consult and Denote (~consult-denote~)]]).
|
|
|
|
Denote is a simple note-taking tool for Emacs. It is based on the idea
|
|
that notes should follow a predictable and descriptive file-naming
|
|
scheme. The file name must offer a clear indication of what the note is
|
|
about, without reference to any other metadata. Denote basically
|
|
streamlines the creation of such files while providing facilities to
|
|
link between them.
|
|
|
|
Denote's file-naming scheme is not limited to "notes". It can be used
|
|
for all types of file, including those that are not editable in Emacs,
|
|
such as videos. Naming files in a consistent way makes their
|
|
filtering and retrieval considerably easier. Denote provides relevant
|
|
facilities to rename files, regardless of file type.
|
|
#+end_quote
|
|
|
|
Prot is the developer and maintainer of this package.
|
|
|
|
+ Package name (GNU ELPA): ~denote~
|
|
+ Official manual: <https://protesilaos.com/emacs/denote>
|
|
+ Change log: <https://protesilaos.com/emacs/denote-changelog>
|
|
+ Git repositories:
|
|
- GitHub: <https://github.com/protesilaos/denote>
|
|
- GitLab: <https://gitlab.com/protesilaos/denote>
|
|
+ Video demo: <https://protesilaos.com/codelog/2022-06-18-denote-demo/>
|
|
+ Backronyms: Denote Everything Neatly; Omit The Excesses. Don't Ever
|
|
Note Only The Epiphenomenal.
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
;;; Denote (simple note-taking and file-naming)
|
|
|
|
;; Read the manual: <https://protesilaos.com/emacs/denote>. This does
|
|
;; not include all the useful features of Denote. I have a separate
|
|
;; private setup for those, as I need to test everything is in order.
|
|
(use-package denote
|
|
:ensure t
|
|
:hook
|
|
;; If you use Markdown or plain text files you want to fontify links
|
|
;; upon visiting the file (Org renders links as buttons right away).
|
|
((text-mode . denote-fontify-links-mode-maybe)
|
|
|
|
;; Highlight Denote file names in Dired buffers. Below is the
|
|
;; generic approach, which is great if you rename files Denote-style
|
|
;; in lots of places as I do.
|
|
;;
|
|
;; If you only want the `denote-dired-mode' in select directories,
|
|
;; then modify the variable `denote-dired-directories' and use the
|
|
;; following instead:
|
|
;;
|
|
;; (dired-mode . denote-dired-mode-in-directories)
|
|
(dired-mode . denote-dired-mode))
|
|
:bind
|
|
;; Denote DOES NOT define any key bindings. This is for the user to
|
|
;; decide. Here I only have a subset of what Denote offers.
|
|
( :map global-map
|
|
("C-c n n" . denote)
|
|
("C-c n N" . denote-type)
|
|
("C-c n o" . denote-sort-dired) ; "order" mnemonic
|
|
;; Note that `denote-rename-file' can work from any context, not
|
|
;; just Dired buffers. That is why we bind it here to the
|
|
;; `global-map'.
|
|
;;
|
|
;; Also see `denote-rename-file-using-front-matter' further below.
|
|
("C-c n r" . denote-rename-file)
|
|
;; If you intend to use Denote with a variety of file types, it is
|
|
;; easier to bind the link-related commands to the `global-map', as
|
|
;; shown here. Otherwise follow the same pattern for
|
|
;; `org-mode-map', `markdown-mode-map', and/or `text-mode-map'.
|
|
:map text-mode-map
|
|
("C-c n i" . denote-link) ; "insert" mnemonic
|
|
("C-c n I" . denote-add-links)
|
|
("C-c n b" . denote-backlinks)
|
|
;; Also see `denote-rename-file' further above.
|
|
("C-c n R" . denote-rename-file-using-front-matter)
|
|
:map org-mode-map
|
|
("C-c n d l" . denote-org-extras-dblock-insert-links)
|
|
("C-c n d b" . denote-org-extras-dblock-insert-backlinks)
|
|
;; Key bindings specifically for Dired.
|
|
:map dired-mode-map
|
|
("C-c C-d C-i" . denote-dired-link-marked-notes)
|
|
("C-c C-d C-r" . denote-dired-rename-marked-files)
|
|
("C-c C-d C-k" . denote-dired-rename-marked-files-with-keywords)
|
|
("C-c C-d C-f" . denote-dired-rename-marked-files-using-front-matter))
|
|
:config
|
|
;; Remember to check the doc strings of those variables.
|
|
(setq denote-directory (expand-file-name "~/Documents/notes/"))
|
|
(setq denote-file-type 'text) ; Org is the default file type
|
|
|
|
;; If you want to have a "controlled vocabulary" of keywords,
|
|
;; meaning that you only use a predefined set of them, then you want
|
|
;; `denote-infer-keywords' to be nil and `denote-known-keywords' to
|
|
;; have the keywords you need.
|
|
(setq denote-known-keywords '("emacs" "philosophy" "politics" "economics"))
|
|
(setq denote-infer-keywords t)
|
|
(setq denote-sort-keywords t)
|
|
|
|
(setq denote-excluded-directories-regexp nil)
|
|
(setq denote-date-format nil) ; read its doc string
|
|
(setq denote-rename-confirmations nil) ; CAREFUL with this if you are not familiar with Denote!
|
|
|
|
(setq denote-backlinks-show-context nil)
|
|
|
|
(setq denote-rename-buffer-format "[D] %t%b")
|
|
(setq denote-buffer-has-backlinks-string " (<--->)")
|
|
|
|
;; Automatically rename Denote buffers when opening them so that
|
|
;; instead of their long file name they have a literal "[D]"
|
|
;; followed by the file's title. Read the doc string of
|
|
;; `denote-rename-buffer-format' for how to modify this.
|
|
(denote-rename-buffer-mode 1)
|
|
|
|
;; ----- PERSONAL TWEAKS FOR EXPERIMENTS -----
|
|
(setq denote-text-front-matter "title: %s\n\n")
|
|
|
|
(defun prot/denote-add-text-front-matter-separator ()
|
|
"Add separator equal to the length of the title.
|
|
Do this when the `denote-file-type' is `text'."
|
|
(when (and (eq denote-file-type 'text)
|
|
;; Not `string=' because there may be a .gpg extension as well.
|
|
(string-match-p (file-name-extension buffer-file-name) "txt"))
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(when (re-search-forward "title:" nil t)
|
|
(let ((text (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
|
|
(if (re-search-forward "^$" nil t)
|
|
(insert (make-string (length text) ?-))
|
|
(error "Could not find an empty line after the front matter")))))))
|
|
|
|
(add-hook 'denote-after-new-note-hook #'prot/denote-add-text-front-matter-separator))
|
|
#+end_src
|
|
|
|
*** The =unravel-langs.el= integration between Consult and Denote (~consult-denote~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:ee82e629-fb05-4c75-9175-48a760a25691
|
|
:END:
|
|
|
|
#+begin_quote
|
|
This is another package of mine which extends my ~denote~ package
|
|
([[#h:e86a66dc-7ef9-4f09-ad7e-946de2034e8d][The =unravel-langs.el= settings for ~denote~ (notes and file-naming)]]).
|
|
|
|
This is glue code to integrate ~denote~ with Daniel Mendler's
|
|
~consult~ ([[#h:22e97b4c-d88d-4deb-9ab3-f80631f9ff1d][The =unravel-completion.el= settings for ~consult~]]). The
|
|
idea is to enhance minibuffer interactions, such as by providing a
|
|
preview of the file-to-linked/opened and by adding more sources to the
|
|
~consult-buffer~ command.
|
|
#+end_quote
|
|
|
|
Prot is the developer of this package.
|
|
|
|
+ Package name (GNU ELPA): ~consult-denote~
|
|
+ Official manual: not available yet.
|
|
+ Git repositories:
|
|
+ GitHub: <https://github.com/protesilaos/consult-denote>
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
(use-package consult-denote
|
|
:ensure t
|
|
:bind
|
|
(("C-c n f" . consult-denote-find)
|
|
("C-c n g" . consult-denote-grep))
|
|
:config
|
|
(consult-denote-mode 1))
|
|
#+end_src
|
|
|
|
** Finally, we provide the ~unravel-langs.el~ module
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-langs.el"
|
|
(provide 'unravel-langs)
|
|
#+end_src
|