These need significant changes to match my workflows, but without the base settings in place doing any work is really difficult. So copying over the basics, I will re-arrange and update everything in later commits.
3685 lines
142 KiB
Org Mode
3685 lines
142 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" "custom-lisp"))
|
|
#+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
|
|
|
|
* Custom libraries
|
|
** The =prot-embark.el= library
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:fb034be5-c316-4c4f-a46f-cebcab332a47
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "custom-lisp/prot-embark.el" :mkdirp yes
|
|
;;; prot-embark.el --- Custom Embark keymaps -*- lexical-binding: t -*-
|
|
|
|
;; Copyright (C) 2023-2024 Protesilaos Stavrou
|
|
|
|
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
|
;; URL: https://protesilaos.com/emacs/dotemacs
|
|
;; Version: 0.1.0
|
|
;; Package-Requires: ((emacs "30.1") (embark "0.23"))
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
;; This program is free software; you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation, either version 3 of the License, or (at
|
|
;; your option) any later version.
|
|
;;
|
|
;; This program is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
;;
|
|
;; Remember that every piece of Elisp that I write is for my own
|
|
;; educational and recreational purposes. I am not a programmer and I
|
|
;; do not recommend that you copy any of this if you are not certain of
|
|
;; what it does.
|
|
|
|
;;; Code:
|
|
|
|
(require 'embark)
|
|
|
|
(defvar-keymap prot-embark-general-map
|
|
:parent embark-general-map
|
|
"i" #'embark-insert
|
|
"w" #'embark-copy-as-kill
|
|
"E" #'embark-export
|
|
"S" #'embark-collect
|
|
"A" #'embark-act-all
|
|
"DEL" #'delete-region)
|
|
|
|
(defvar-keymap prot-embark-url-map
|
|
:parent embark-general-map
|
|
"b" #'browse-url
|
|
"d" #'embark-download-url
|
|
"e" #'eww)
|
|
|
|
(defvar-keymap prot-embark-buffer-map
|
|
:parent embark-general-map
|
|
"k" #'prot-simple-kill-buffer
|
|
"o" #'switch-to-buffer-other-window
|
|
"e" #'ediff-buffers)
|
|
|
|
(add-to-list 'embark-post-action-hooks (list 'prot-simple-kill-buffer 'embark--restart))
|
|
|
|
(defvar-keymap prot-embark-file-map
|
|
:parent embark-general-map
|
|
"f" #'find-file
|
|
"j" #'embark-dired-jump
|
|
"c" #'copy-file
|
|
"e" #'ediff-files)
|
|
|
|
(defvar-keymap prot-embark-identifier-map
|
|
:parent embark-general-map
|
|
"h" #'display-local-help
|
|
"." #'xref-find-definitions
|
|
"o" #'occur)
|
|
|
|
(defvar-keymap prot-embark-command-map
|
|
:parent embark-general-map
|
|
"h" #'describe-command
|
|
"." #'embark-find-definition)
|
|
|
|
(defvar-keymap prot-embark-expression-map
|
|
:parent embark-general-map
|
|
"e" #'pp-eval-expression
|
|
"m" #'pp-macroexpand-expression)
|
|
|
|
(defvar-keymap prot-embark-function-map
|
|
:parent embark-general-map
|
|
"h" #'describe-function
|
|
"." #'embark-find-definition)
|
|
|
|
(defvar-keymap prot-embark-package-map
|
|
:parent embark-general-map
|
|
"h" #'describe-package
|
|
"i" #'package-install
|
|
"d" #'package-delete
|
|
"r" #'package-reinstall
|
|
"u" #'embark-browse-package-url
|
|
"w" #'embark-save-package-url)
|
|
|
|
(defvar-keymap prot-embark-symbol-map
|
|
:parent embark-general-map
|
|
"h" #'describe-symbol
|
|
"." #'embark-find-definition)
|
|
|
|
(defvar-keymap prot-embark-variable-map
|
|
:parent embark-general-map
|
|
"h" #'describe-variable
|
|
"." #'embark-find-definition)
|
|
|
|
(defvar-keymap prot-embark-region-map
|
|
:parent embark-general-map
|
|
"a" #'align-regexp
|
|
"D" #'delete-duplicate-lines
|
|
"f" #'flush-lines
|
|
"i" #'epa-import-keys-region
|
|
"d" #'epa-decrypt-armor-in-region
|
|
"r" #'repunctuate-sentences
|
|
"s" #'sort-lines
|
|
"u" #'untabify)
|
|
|
|
;; The minimal indicator shows cycling options, but I have no use
|
|
;; for those. I want it to be silent.
|
|
(defun prot-embark-no-minimal-indicator ())
|
|
(advice-add #'embark-minimal-indicator :override #'prot-embark-no-minimal-indicator)
|
|
|
|
(defun prot-embark-act-no-quit ()
|
|
"Call `embark-act' but do not quit after the action."
|
|
(interactive)
|
|
(let ((embark-quit-after-action nil))
|
|
(call-interactively #'embark-act)))
|
|
|
|
(defun prot-embark-act-quit ()
|
|
"Call `embark-act' and quit after the action."
|
|
(interactive)
|
|
(let ((embark-quit-after-action t))
|
|
(call-interactively #'embark-act))
|
|
(when (and (> (minibuffer-depth) 0)
|
|
(derived-mode-p 'completion-list-mode))
|
|
(abort-recursive-edit)))
|
|
|
|
(provide 'prot-embark)
|
|
;;; prot-embark.el ends here
|
|
#+end_src
|
|
|
|
** The =unravel-git.el= module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:65e3eff5-0bff-4e1f-b6c5-0d3aa1a0d232
|
|
:END:
|
|
|
|
[ Watch Prot's talk: [[https://protesilaos.com/codelog/2023-08-03-contribute-core-emacs/][Contribute to GNU Emacs core]] (2023-08-03). ]
|
|
|
|
#+begin_quote
|
|
This section covers my settings for version control per se, but more
|
|
widely for tools related to checking different versions of files and
|
|
working with so-called "projects".
|
|
#+end_quote
|
|
|
|
*** The =unravel-git.el= section about ediff
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:89edea05-4d94-4ea1-b2a8-5ad01422618c
|
|
:END:
|
|
|
|
[ Watch Prot's talk: [[https://protesilaos.com/codelog/2023-11-17-emacs-ediff-basics/][Emacs: ediff basics]] (2023-12-30) ]
|
|
|
|
#+begin_quote
|
|
The built-in ~ediff~ feature provides several commands that let us
|
|
compare files or buffers side-by-side. The defaults of ~ediff~ are bad,
|
|
in my opinion: it puts buffers one on top of the other and places the
|
|
"control panel" in a separate Emacs frame. The first time I tried to
|
|
use it, I thought I broke my setup because it is unlike anything we
|
|
normally interact with. As such, the settings I have for
|
|
~ediff-split-window-function~ and ~ediff-window-setup-function~ are
|
|
what I would expect Emacs maintainers to adopt as the new default. I
|
|
strongly encourage everyone to start with them.
|
|
|
|
In my workflow, the points of entry to the ~ediff~ feature are the
|
|
commands ~ediff-files~, ~ediff-buffers~. Sometimes I use the 3-way
|
|
variants with ~ediff-files3~ and ~ediff-buffers3~, though this is rare.
|
|
Do watch the video I link to in the beginning of this section, as it
|
|
covers the main functionality of this neat tool. I also show how it
|
|
integrates with ~magit~ ([[#h:b08af527-9ebf-4425-ac3a-24b4f371a4fd][The =unravel-git.el= section about ~magit~ (great Git client)]]).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-git.el" :mkdirp yes
|
|
;;;; `ediff'
|
|
(use-package ediff
|
|
:ensure nil
|
|
:commands (ediff-buffers ediff-files ediff-buffers3 ediff-files3)
|
|
:init
|
|
(setq ediff-split-window-function 'split-window-horizontally)
|
|
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
|
|
:config
|
|
(setq ediff-keep-variants nil)
|
|
(setq ediff-make-buffers-readonly-at-startup nil)
|
|
(setq ediff-merge-revisions-with-ancestor t)
|
|
(setq ediff-show-clashes-only t))
|
|
#+end_src
|
|
|
|
*** The =unravel-git.el= section about =project.el=
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:7dcbcadf-8af6-487d-b864-e4ce56d69530
|
|
:END:
|
|
|
|
#+begin_quote
|
|
In Emacs parlance, a "project" is a collection of files and/or
|
|
directories that share the same root. The root of a project is
|
|
identified by a special file or directory, with =.git/= being one of
|
|
the defaults as it is a version control system supported by the
|
|
built-in =vc.el= ([[#h:50add1d8-f0f4-49be-9e57-ab280a4aa300][The =unravel-git.el= section about =vc.el= and related]]).
|
|
|
|
We can specify more project roots as a list of strings in the user
|
|
option ~project-vc-extra-root-markers~. I work exclusively with Git
|
|
repositories, so I just add there a =.project= file in case I ever
|
|
need to register a project without it being controlled by ~git~. In
|
|
that case, the =.project= file is just an empty file in a directory
|
|
that I want to treat as the root of this project.
|
|
|
|
The common way to switch to a project is to type =C-x p p=, which
|
|
calls the command ~project-switch-project~. It lists all registered
|
|
projects and also includes a =... (choose a dir)= option. By choosing
|
|
a new directory, we register it in our project list if it has a
|
|
recognisable root. Once we select a project, we are presented with a
|
|
list of common actions to start working on the project. These are
|
|
defined in the user option ~project-switch-commands~ and are activated
|
|
by the final key that accesses them from the =C-x p= prefix. As such,
|
|
do =M-x describe-keymap= and check the ~project-prefix-map~. For
|
|
example, I bind ~project-dired~ to =C-x p RET=, so =RET= accesses this
|
|
command after =C-x p p= as well.
|
|
|
|
If any of the =project.el= commands is called from outside a project,
|
|
it first prompts for a project and then carries out its action. For
|
|
example, ~project-find-file~ will ask for a project to use, then
|
|
switch to it, and then prompt for a file inside of the specified
|
|
project.
|
|
|
|
While inside a project, we have many commands that operate on the
|
|
project level. For example, =C-x p f= (~project-find-file~) searches
|
|
for a file across the project, while =C-x p b= (~project-switch-to-buffer~)
|
|
switches to a buffer that is specific to the project. Again, check the
|
|
~project-prefix-map~ for available commands.
|
|
|
|
If not inside a project, the project-related commands will first
|
|
prompt to select a project (same as typing =C-x p p=) and then carry
|
|
out their action.
|
|
|
|
I combine projects with my ~beframe~ package, so that when I switch to
|
|
a project I get a new frame that limits the buffers I visit there
|
|
limited to that frame ([[#h:77e4f174-0c86-460d-8a54-47545f922ae9][The =unravel-window.el= section about ~beframe~]]).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-git.el"
|
|
;;;; `project'
|
|
(use-package project
|
|
:ensure nil
|
|
:bind
|
|
(("C-x p ." . project-dired)
|
|
("C-x p C-g" . keyboard-quit)
|
|
("C-x p <return>" . project-dired)
|
|
("C-x p <delete>" . project-forget-project))
|
|
:config
|
|
(setopt project-switch-commands
|
|
'((project-find-file "Find file")
|
|
(project-find-regexp "Find regexp")
|
|
(project-find-dir "Find directory")
|
|
(project-dired "Root dired")
|
|
(project-vc-dir "VC-Dir")
|
|
(project-shell "Shell")
|
|
(keyboard-quit "Quit")))
|
|
(setq project-vc-extra-root-markers '(".project")) ; Emacs 29
|
|
(setq project-key-prompt-style t) ; Emacs 30
|
|
|
|
(advice-add #'project-switch-project :after #'prot-common-clear-minibuffer-message))
|
|
#+end_src
|
|
|
|
*** The =unravel-git.el= section about ~diff-mode~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:8b426a69-e3cd-42ac-8788-f41f6629f879
|
|
:END:
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-git.el"
|
|
;;;; `diff-mode'
|
|
(use-package diff-mode
|
|
:ensure nil
|
|
:defer t
|
|
:config
|
|
(setq diff-default-read-only t)
|
|
(setq diff-advance-after-apply-hunk t)
|
|
(setq diff-update-on-the-fly t)
|
|
;; The following are from Emacs 27.1
|
|
(setq diff-refine nil) ; I do it on demand, with my `agitate' package (more below)
|
|
(setq diff-font-lock-prettify t) ; I think nil is better for patches, but let me try this for a while
|
|
(setq diff-font-lock-syntax 'hunk-also))
|
|
#+end_src
|
|
|
|
*** The =unravel-git.el= section about ~magit~ (great Git client)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:b08af527-9ebf-4425-ac3a-24b4f371a4fd
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~magit~ package, maintained by Jonas Bernoulli, is the best
|
|
front-end to ~git~ I have ever used. Not only is it excellent at
|
|
getting the job done, it also helps you learn more about what ~git~
|
|
has to offer.
|
|
|
|
At the core of its interface is ~transient~. This is a library that
|
|
was originally developed as Magit-specific code that was then
|
|
abstracted away and ultimately incorporated into Emacs version 29.
|
|
With ~transient~, we get a window pop up with keys and commands
|
|
corresponding to them. The window is interactive, as the user can set
|
|
a value or toggle an option and have it take effect when the relevant
|
|
command is eventually invoked. For ~git~, in particular, this
|
|
interface is a genious way to surface the plethora of options.
|
|
|
|
To start, call the command ~magit-status~. It brings up a buffer that
|
|
shows information about the state of the repository. Sections include
|
|
an overview of the current =HEAD=, untracked files, unstaged changes,
|
|
staged changes, and recent commits. Each section's visibility state
|
|
can be cycled by pressing =TAB= (variations of this are available---remember
|
|
to do =C-h m= (~describe-mode~) in an unfamiliar major mode to get
|
|
information about its key bindings).
|
|
|
|
From the status buffer, we can perform all the usual version control
|
|
operations. By typing =?= (~magit-dispatch~), we bring up the main
|
|
~transient~ menu, with keys that then bring up their own submenus,
|
|
such as for viewing commit logs, setting the remotes, switching
|
|
branches, etc.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-git.el"
|
|
;;; Interactive and powerful git front-end (Magit)
|
|
(use-package transient
|
|
:defer t
|
|
:config
|
|
(setq transient-show-popup 0.5))
|
|
|
|
(use-package magit
|
|
:ensure t
|
|
:bind ("C-c g" . magit-status)
|
|
:init
|
|
(setq magit-define-global-key-bindings nil)
|
|
;; (setq magit-section-visibility-indicator '("⮧"))
|
|
:config
|
|
(setq git-commit-summary-max-length 50)
|
|
;; NOTE 2023-01-24: I used to also include `overlong-summary-line'
|
|
;; in this list, but I realised I do not need it. My summaries are
|
|
;; always in check. When I exceed the limit, it is for a good
|
|
;; reason.
|
|
;; (setq git-commit-style-convention-checks '(non-empty-second-line))
|
|
|
|
(setq magit-diff-refine-hunk t))
|
|
|
|
(use-package magit-repos
|
|
:ensure nil ; part of `magit'
|
|
:commands (magit-list-repositories)
|
|
:init
|
|
(setq magit-repository-directories
|
|
'(("~/src/prototypes" . 1))))
|
|
#+end_src
|
|
|
|
*** The =unravel-git.el= call to ~provide~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:4e7035c5-9350-4c51-be85-85f2539ed295
|
|
:END:
|
|
|
|
Finally, we ~provide~ the module.
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-git.el"
|
|
(provide 'unravel-git)
|
|
#+end_src
|
|
|
|
** The =unravel-org.el= module
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:d799c3c0-bd6a-40bb-bd1a-ba4ea5367840
|
|
:END:
|
|
|
|
Watch these talks by Prot:
|
|
|
|
- [[https://protesilaos.com/codelog/2023-12-18-emacs-org-advanced-literate-conf/][Advanced literate configuration with Org]] (2023-12-18)
|
|
- [[https://protesilaos.com/codelog/2023-05-23-emacs-org-basics/][Basics of Org mode]] (2023-05-23)
|
|
- [[https://protesilaos.com/codelog/2021-12-09-emacs-org-block-agenda/][Demo of my custom Org block agenda]] (2021-12-09)
|
|
- [[https://protesilaos.com/codelog/2020-02-04-emacs-org-capture-intro/][Primer on "org-capture"]] (2020-02-04)
|
|
|
|
#+begin_quote
|
|
At its core, Org is a plain text markup language. By "markup
|
|
language", we refer to the use of common characters to apply styling,
|
|
such as how a word wrapped in asterisks acquires strong emphasis.
|
|
Check the video I link to above on the basics of Org mode.
|
|
|
|
Though what makes Org powerful is not the markup per se, but the fact
|
|
that it has a rich corpus of Emacs Lisp code that does a lot with this
|
|
otherwise plain text notation. Some of the headline features:
|
|
|
|
- Cycle the visibility of any heading and its subheadings. This lets
|
|
you quickly fold a section you do not need to see (or reveal the one
|
|
you care about).
|
|
- Mix prose with code in a single document to either make the whole
|
|
thing an actual program or to evaluate/demonstrate some snippets.
|
|
- Convert ("export") an Org file to a variety of formats, including
|
|
HTML and PDF.
|
|
- Use LaTeX inside of Org files to produce a scientific paper without
|
|
all the markup of LaTeX.
|
|
- Manage TODO lists and implement a concomitant methodology of
|
|
labelling task states.
|
|
- Quickly shift a "thing" (heading, list item, paragraph, ...) further
|
|
up or down in the file.
|
|
- Use tables with formulas as a lightweight alternative to spreadsheet
|
|
software.
|
|
- Capture data or fleeting thoughts efficiently using templates.
|
|
- Maintain an agenda for all your date-bound activities.
|
|
- Clock in and out of tasks, to eventually track how you are spending
|
|
your time.
|
|
- Link to files regardless of file type. This includes special links
|
|
such as to an Info manual or an email, if you also have that running
|
|
locally and integrated with Emacs ([[#h:755e195b-9471-48c7-963b-33055969b4e2][The =unravel-email.el= module]]).
|
|
|
|
In other words, Org is highly capable and widely considered one of the
|
|
killer apps of Emacs.
|
|
|
|
This section covers the relevant configurations. You will notice that
|
|
it is not limited to Org, as some other built-in features are also
|
|
relevant here.
|
|
#+end_quote
|
|
|
|
*** The =unravel-org.el= section on the ~calendar~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:94d48381-1711-4d6b-8449-918bc1e3836c
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The ~calendar~ is technically independent of Org, though it tightly
|
|
integrates with it. We witness this when we are setting timestamps,
|
|
such as while setting a =SCHEDULED= or =DEADLINE= entry for a given
|
|
heading. All I do here is set some stylistic preferences.
|
|
|
|
Note that Emacs also has a ~diary~ command. I used it for a while, but
|
|
Org is far more capable, so I switched to it completely.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el" :mkdirp yes
|
|
;;; Calendar
|
|
(use-package calendar
|
|
:ensure nil
|
|
:commands (calendar)
|
|
:config
|
|
(setq calendar-mark-diary-entries-flag nil)
|
|
(setq calendar-mark-holidays-flag t)
|
|
(setq calendar-mode-line-format nil)
|
|
(setq calendar-time-display-form
|
|
'( 24-hours ":" minutes
|
|
(when time-zone (format "(%s)" time-zone))))
|
|
(setq calendar-week-start-day 1) ; Monday
|
|
(setq calendar-date-style 'iso)
|
|
(setq calendar-time-zone-style 'numeric) ; Emacs 28.1
|
|
|
|
(require 'solar)
|
|
(setq calendar-latitude 35.17 ; Not my actual coordinates
|
|
calendar-longitude 33.36)
|
|
|
|
(require 'cal-dst)
|
|
(setq calendar-standard-time-zone-name "+0200")
|
|
(setq calendar-daylight-time-zone-name "+0300"))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= section about appointment reminders (=appt.el=)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:bd4b0dcb-a925-4bd7-90db-6379a7ca6f5e
|
|
:END:
|
|
|
|
#+begin_quote
|
|
The built in =appt.el= defines functionality for handling
|
|
notifications about appointments. It is originally designed to work
|
|
with the generic diary feature (the =M-x diary= one, I mean), which I
|
|
do not use anymore, but also integrates nicely with the Org agenda
|
|
([[#h:7fe87b83-2815-4617-a5f9-d3417dd9d248][The =unravel-org.el= Org agenda settings]]). I deepen this
|
|
integration further, such that after adding a task or changing its
|
|
state, the appointments mechanism re-reads my data to register new
|
|
notifications. This is done via a series of hooks and with the use of
|
|
the advice feature of Emacs Lisp.
|
|
|
|
Here I am setting some simple settings to keep appointment notifations
|
|
minimal. I do not need them to inform me about the contents of my next
|
|
entry on the agenda: just show text on the mode line telling me how
|
|
many minutes are left until the event.
|
|
|
|
In Org files, every heading can have an =APPT_WARNTIME= property: it takes
|
|
a numeric value representing minutes for a forewarning from =appt.el=.
|
|
I use this in tandem with ~org-capture~ for tasks that need to be
|
|
done at a specific time, such as coaching sessions ([[#h:f8f06938-0dfe-45c3-b4cf-996d36cba82d][The =unravel-org.el= Org capture templates (~org-capture~)]]).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;; Appt (appointment reminders which also integrate with Org agenda)
|
|
(use-package appt
|
|
:ensure nil
|
|
:commands (appt-activate)
|
|
:config
|
|
(setq appt-display-diary nil
|
|
appt-display-format nil
|
|
appt-display-mode-line t
|
|
appt-display-interval 3
|
|
appt-audible nil ; TODO 2023-01-25: t does nothing because I disable `ring-bell-function'?
|
|
appt-warning-time-regexp "appt \\([0-9]+\\)" ; This is for the diary
|
|
appt-message-warning-time 6)
|
|
|
|
(with-eval-after-load 'org-agenda
|
|
(appt-activate 1)
|
|
|
|
;; NOTE 2021-12-07: In my `prot-org.el' (see further below), I add
|
|
;; `org-agenda-to-appt' to various relevant hooks.
|
|
;;
|
|
;; Create reminders for tasks with a due date when this file is read.
|
|
(org-agenda-to-appt)))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= section with basic Org settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:e03df1e0-b43e-49b5-978e-6a511165617c
|
|
:END:
|
|
|
|
#+begin_quote
|
|
Org, also known as "Org mode", is one of the potentially most useful
|
|
feature sets available to every Emacs user. At its core, Org is a
|
|
lightweight markup language: you can have headings and paragraphs,
|
|
mark a portion of text with emphasis, produce bullet lists, include
|
|
code blocks, and the like. Though what really sets Org apart from
|
|
other markup languages is the rich corpus of Emacs Lisp written around
|
|
it to do all sorts of tasks with this otherwise plain text format.
|
|
|
|
With Org you can write technical documents (e.g. the manuals of all my
|
|
Emacs packages), maintain a simple or highly sophisticated system for
|
|
task management, organise your life using the agenda, write tables
|
|
that can evaluate formulas to have spreadsheet functionality, have
|
|
embedded LaTeX, evaluate code blocks in a wide range of programming
|
|
languages and reuse their results for literate programming, include
|
|
the contents of other files into a singular file, use one file to
|
|
generate other files/directories with all their contents, and export
|
|
the Org document to a variety of formats like =.pdf= and =.odt=.
|
|
Furthermore, Org can be used as a lightweight, plain text database, as
|
|
each heading can have its own metadata. This has practical
|
|
applications in most of the aforementioned.
|
|
|
|
In short, if something can be done with plain text, Org probably does
|
|
it already or has all the elements for piecing it together. This
|
|
document, among many of my published works, is testament to Org's
|
|
sheer power, which I explained at greater length in a video
|
|
demonstration: [[https://protesilaos.com/codelog/2023-12-18-emacs-org-advanced-literate-conf/][Advanced literate configuration with Org]] (2023-12-18).
|
|
|
|
This being Emacs, everything is customisable and Org is a good example
|
|
of this. There are a lot of user options for us to tweak things to our
|
|
liking. I do as much, though know that Org is perfectly usable without
|
|
any configuration. The following sections contain further commentary
|
|
on how I use Org.
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;; Org-mode (personal information manager)
|
|
(use-package org
|
|
:ensure nil
|
|
:init
|
|
(setq org-directory (expand-file-name "~/Documents/org/"))
|
|
(setq org-imenu-depth 7)
|
|
|
|
(add-to-list 'safe-local-variable-values '(org-hide-leading-stars . t))
|
|
(add-to-list 'safe-local-variable-values '(org-hide-macro-markers . t))
|
|
:bind
|
|
( :map global-map
|
|
("C-c l" . org-store-link)
|
|
("C-c o" . org-open-at-point-global)
|
|
:map org-mode-map
|
|
;; I don't like that Org binds one zillion keys, so if I want one
|
|
;; for something more important, I disable it from here.
|
|
("C-'" . nil)
|
|
("C-," . nil)
|
|
("M-;" . nil)
|
|
("C-c M-l" . org-insert-last-stored-link)
|
|
("C-c C-M-l" . org-toggle-link-display)
|
|
("M-." . org-edit-special) ; alias for C-c ' (mnenomic is global M-. that goes to source)
|
|
:map org-src-mode-map
|
|
("M-," . org-edit-src-exit) ; see M-. above
|
|
:map narrow-map
|
|
("b" . org-narrow-to-block)
|
|
("e" . org-narrow-to-element)
|
|
("s" . org-narrow-to-subtree)
|
|
:map ctl-x-x-map
|
|
("i" . prot-org-id-headlines)
|
|
("h" . prot-org-ox-html))
|
|
:config
|
|
;; My custom extras, which I use for the agenda and a few other Org features.
|
|
(require 'prot-org)
|
|
|
|
;;;; general settings
|
|
(setq org-ellipsis "⮧")
|
|
(setq org-adapt-indentation nil) ; No, non, nein, όχι!
|
|
(setq org-special-ctrl-a/e nil)
|
|
(setq org-special-ctrl-k nil)
|
|
(setq org-M-RET-may-split-line '((default . nil)))
|
|
(setq org-hide-emphasis-markers nil)
|
|
(setq org-hide-macro-markers nil)
|
|
(setq org-hide-leading-stars nil)
|
|
(setq org-cycle-separator-lines 0)
|
|
(setq org-structure-template-alist
|
|
'(("s" . "src")
|
|
("e" . "src emacs-lisp")
|
|
("E" . "src emacs-lisp :results value code :lexical t")
|
|
("t" . "src emacs-lisp :tangle FILENAME")
|
|
("T" . "src emacs-lisp :tangle FILENAME :mkdirp yes")
|
|
("x" . "example")
|
|
("X" . "export")
|
|
("q" . "quote")))
|
|
(setq org-fold-catch-invisible-edits 'show)
|
|
(setq org-return-follows-link nil)
|
|
(setq org-loop-over-headlines-in-active-region 'start-level)
|
|
(setq org-modules '(ol-info ol-eww))
|
|
(setq org-use-sub-superscripts '{})
|
|
(setq org-insert-heading-respect-content t)
|
|
(setq org-read-date-prefer-future 'time)
|
|
(setq org-highlight-latex-and-related nil) ; other options affect elisp regexp in src blocks
|
|
(setq org-fontify-quote-and-verse-blocks t)
|
|
(setq org-fontify-whole-block-delimiter-line t)
|
|
(setq org-track-ordered-property-with-tag t)
|
|
(setq org-highest-priority ?A)
|
|
(setq org-lowest-priority ?C)
|
|
(setq org-default-priority ?A)
|
|
(setq org-priority-faces nil))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org to-do and refile settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:024dd541-0061-4a10-b10b-b17dcd4794b9
|
|
:END:
|
|
|
|
#+begin_quote
|
|
One of the many use-cases for Org is to maintain a plain text to-do
|
|
list. A heading that starts with a to-do keyword, such as =TODO= is
|
|
treated as a task and its state is considered not completed.
|
|
|
|
We can switch between the task states with shift and the left or right
|
|
arrow keys. Or we can select a keyword directly with =C-c C-t=, which
|
|
calls ~org-todo~ by default. I personally prefer the latter approach,
|
|
as it is more precise.
|
|
|
|
Whenever a task state changes, we can log that event in a special
|
|
=LOGBOOK= drawer. This is automatically placed right below the
|
|
heading, before any paragraph text. Logging data is an opt-in feature,
|
|
which I consider helpful ([[#h:0884658e-9eb5-47e3-9338-66e09004a1a0][The =unravel-org.el= Org time/state logging]]).
|
|
|
|
Tasks can be associated with timestamps, typically a scheduled
|
|
date+time or a deadline+time. This can be helpful when we are
|
|
reviewing the source Org file, though it really shines in tandem with
|
|
the agenda. Any heading that has a timestamp and which belongs to a
|
|
file in the ~org-agenda-files~ will show up on the agenda in the given
|
|
date ([[#h:7fe87b83-2815-4617-a5f9-d3417dd9d248][The =unravel-org.el= Org agenda settings]]).
|
|
|
|
By default, the ~org-todo-keywords~ are =TODO= and =DONE=. We can
|
|
write more keywords if we wish to implement a descriptive workflow.
|
|
For example, we can have a =WAIT= keyword for something that is to be
|
|
done but is not actionable yet. While the number of keywords is not
|
|
limited, the binary model is the same: we have words that represent
|
|
the incomplete state and those that count as the completion of the
|
|
task. For instance, both =CANCEL= and =DONE= mean that the task is not
|
|
actionable anymore and we move on to other things. As such, the extra
|
|
keywords are a way for the user to make tasks more descriptive and
|
|
easy to find. In the value of the ~org-todo-keywords~, we use the bar
|
|
character to separate the incomplete state to the left from the
|
|
completed one to the right.
|
|
|
|
One of the agenda's headiline features is the ability to produce a
|
|
view that lists headings with the given keyword. So having the right
|
|
terms can make search and retrieval of data more easy. On the
|
|
flip-side, too many keywords add cognitive load and require more
|
|
explicit search terms to yield the desired results. I used to work
|
|
with a more descriptive set of keywords, but ultimately decided to
|
|
keep things simple.
|
|
|
|
The refile mechanism is how we can reparent a heading, by moving it
|
|
from one place to another. We do this with the command ~org-refile~,
|
|
bound to =C-c C-w= by default. A common workflow where refiling is
|
|
essential is to have an "inbox" file or heading, where unprocessed
|
|
information is stored at, and periodically process its contents to
|
|
move the data where it belongs. Though it can also work fine without
|
|
any such inbox, in those cases where a heading should be stored
|
|
someplace else. The ~org-refile-targets~ specifies the files that are
|
|
available when we try to refile the current heading. With how I set it
|
|
up, all the agenda files plus the current file's headings up to level
|
|
2 are included as possible targets.
|
|
|
|
In terms of workflow, I have not done a refile in a very long time,
|
|
because my entries always stay in the same place as I had envisaged at
|
|
the capture phase ([[#h:f8f06938-0dfe-45c3-b4cf-996d36cba82d][The =unravel-org.el= Org capture templates (~org-capture~)]]).
|
|
#+end_quote
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; refile, todo
|
|
(use-package org
|
|
:ensure nil
|
|
:config
|
|
(setq org-refile-targets
|
|
'((org-agenda-files . (:maxlevel . 2))
|
|
(nil . (:maxlevel . 2))))
|
|
(setq org-refile-use-outline-path t)
|
|
(setq org-refile-allow-creating-parent-nodes 'confirm)
|
|
(setq org-refile-use-cache t)
|
|
(setq org-reverse-note-order nil)
|
|
;; ;; NOTE 2023-04-07: Leaving this here for demo purposes.
|
|
;; (setq org-todo-keywords
|
|
;; '((sequence "TODO(t)" "MAYBE(m)" "WAIT(w@/!)" "|" "CANCEL(c@)" "DONE(d!)")
|
|
;; (sequence "COACH(k)" "|" "COACHED(K!)")))
|
|
(setq org-todo-keywords
|
|
'((sequence "TODO(t)" "|" "CANCEL(c@)" "DONE(d!)")
|
|
(sequence "COACH(k)" "|" "COACHED(K!)")))
|
|
|
|
(defface prot/org-bold-done
|
|
'((t :inherit (bold org-done)))
|
|
"Face for bold DONE-type Org keywords.")
|
|
|
|
(setq org-todo-keyword-faces
|
|
'(("CANCEL" . prot/org-bold-done)))
|
|
(setq org-use-fast-todo-selection 'expert)
|
|
|
|
(setq org-fontify-done-headline nil)
|
|
(setq org-fontify-todo-headline nil)
|
|
(setq org-fontify-whole-heading-line nil)
|
|
(setq org-enforce-todo-dependencies t)
|
|
(setq org-enforce-todo-checkbox-dependencies t))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org heading tags
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:81de4e32-a1af-4e1f-9e10-90eb0c90afa2
|
|
:END:
|
|
|
|
Each Org heading can have one or more tags associated with it, while
|
|
all headings inherit any potential =#+FILETAGS=. We can add tags to a
|
|
heading when the cursor is over it by typing the ever flexible =C-c C-c=.
|
|
Though the more specific ~org-set-tags-command~ also gets the job
|
|
done, plus it does not require that the cursor is positioned on the
|
|
heading text.
|
|
|
|
Tagging is useful for searching and retrieving the data we store. The
|
|
Org agenda, in particular, provides commands to filter tasks by tag:
|
|
|
|
- [[#h:024dd541-0061-4a10-b10b-b17dcd4794b9][The =unravel-org.el= Org to-do and refile settings]]
|
|
- [[#h:7fe87b83-2815-4617-a5f9-d3417dd9d248][The =unravel-org.el= Org agenda settings]]
|
|
|
|
The user option ~org-tag-alist~ lets us specify tags we always want to
|
|
use, though we can write tags per file as well by using the =#+TAGS=
|
|
keyword. I do the latter as a global list of tags is not useful in my
|
|
case. For example, when I wan checking my =coach.org= file for the
|
|
coaching sessions I provide, I do not need to see any of the tags that
|
|
make sense in my general =tasks.org=.
|
|
|
|
Note that in the settings below I disable the auto-alignment that Org
|
|
does where it shifts tags to the right of the heading. I do not like
|
|
it.
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; tags
|
|
(use-package org
|
|
:ensure nil
|
|
:config
|
|
(setq org-tag-alist nil)
|
|
(setq org-auto-align-tags nil)
|
|
(setq org-tags-column 0))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org time/state logging
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:0884658e-9eb5-47e3-9338-66e09004a1a0
|
|
:END:
|
|
|
|
Org can keep a record of state changes, such as when we set an entry
|
|
marked with the =TODO= keyword as =DONE= or when we reschedule an
|
|
appointment ([[#h:7fe87b83-2815-4617-a5f9-d3417dd9d248][The =unravel-org.el= Org agenda settings]]). This data
|
|
is stored in a =LOGBOOK= drawer right below the heading. I choose to
|
|
keep track of this information, as it is sometimes useful to capture
|
|
mistakes or figure out intent in the absence of further clarification
|
|
(though I do tend to write why something happened).
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; log
|
|
(use-package org
|
|
:ensure nil
|
|
:config
|
|
(setq org-log-done 'time)
|
|
(setq org-log-into-drawer t)
|
|
(setq org-log-note-clock-out nil)
|
|
(setq org-log-redeadline 'time)
|
|
(setq org-log-reschedule 'time))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org link settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:da8ce883-7f21-4a6e-a41f-d668ad762b41
|
|
:END:
|
|
|
|
One of the nice things about Org is its flexible linking mechanism. It
|
|
can produce links to a variety of file types or buffers and even
|
|
navigate to a section therein.
|
|
|
|
At its simplest form, we have the =file= link type, which points to a
|
|
file system path, with an optional extension for a match inside the
|
|
file, as documented in the manual. Evaluate this inside of Emacs:
|
|
|
|
#+begin_example emacs-lisp
|
|
(info "(org) Search Options")
|
|
#+end_example
|
|
|
|
Links to buffers are also common and valuable. For example, we can
|
|
have a link to a page produced by the ~man~ command, which gives us
|
|
quick access to the documentation of some program. When Org follows
|
|
that link, it opens the buffer in the appropriate major mode. For me,
|
|
the most common scenario is a link to an email, which I typically
|
|
associate with a task that shows up in my agenda:
|
|
|
|
- [[#h:f8f06938-0dfe-45c3-b4cf-996d36cba82d][The =unravel-org.el= Org capture templates (~org-capture~)]]
|
|
- [[#h:49890997-448e-408d-bebe-2003259bb125][The =unravel-notmuch.el= glue code for ~org-capture~ (=ol-notmuch.el=)]]
|
|
|
|
Org supports lots of link types out-of-the-box, though more can be
|
|
added by packages. My Denote does this: it defines a =denote= link
|
|
type which behaves the same way as the =file= type except that it uses
|
|
the identifier of the file instead of its full path (so eve if the
|
|
file is renamed, the link will work for as long as the identifier
|
|
remains the same).
|
|
|
|
Links can be generated automatically as part of an ~org-capture~
|
|
template. The command ~org-store-link~ produces one manually, storing
|
|
it to a special data structure from which it can be retrieved later
|
|
for insertion with the command ~org-insert-link~. The latter command
|
|
can also create new links, simply by receiving data that is different
|
|
from what was already stored.
|
|
|
|
I bind ~org-store-link~ in main section of the Org configuration:
|
|
[[#h:e03df1e0-b43e-49b5-978e-6a511165617c][The =unravel-org.el= section with basic Org settings]].
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; links
|
|
(use-package org
|
|
:ensure nil
|
|
:config
|
|
(require 'prot-org) ; for the above commands
|
|
|
|
(setq org-link-context-for-files t)
|
|
(setq org-link-keep-stored-after-insertion nil)
|
|
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org code block settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:1f5a0d46-5202-48dd-8048-b48ce17f3df8
|
|
:END:
|
|
|
|
This document benefits from Org's ability to combine prose with code,
|
|
by placing the latter inside of a block that is delimited by
|
|
=#+BEGIN_SRC= and =#+END_SRC= lines.
|
|
|
|
Code blocks can use the syntax highlighting ("fontification" in Emacs
|
|
parlance) of a given major mode. They can also have optional
|
|
parameters passed to their header, which expand the capabilities of
|
|
the block. For instance, the following code block with my actual
|
|
configuration uses the fontification of the ~emacs-lisp-mode~ and has
|
|
a =:tangle= parameter with a value of a file system path. When I
|
|
invoke the command ~org-babel-tangle~, the contents of this block will
|
|
be added to that file, creating the file if necessary.
|
|
|
|
More generally, Org is capable of evaluating code blocks and passing
|
|
their return value to other code blocks. It is thus possible to write
|
|
a fully fledged program as an Org document. This paradigm is known as
|
|
"literate programming". In the case of an Emacs configuration, such as
|
|
mine, it is called a "literate configuration" or variants thereof. I
|
|
did a video about my setup: [[https://protesilaos.com/codelog/2023-12-18-emacs-org-advanced-literate-conf/][Advanced literate configuration with Org]] (2023-12-18).
|
|
|
|
Org can evaluate code blocks in many languages. This is known as "Org
|
|
Babel" and the files which implement support for a given language are
|
|
typically named =ob-LANG.el= where =LANG= is the name of the language.
|
|
We can load the requisite code for the languages we care about with
|
|
something like the following:
|
|
|
|
#+begin_example emacs-lisp
|
|
(require 'ob-python)
|
|
|
|
;; OR
|
|
|
|
(use-package ob-python)
|
|
|
|
;; OR for more control
|
|
|
|
(use-package ob-python
|
|
:after org
|
|
:config
|
|
;; Settings here
|
|
)
|
|
#+end_example
|
|
|
|
I seldom need to work with Org Babel, so I do not load any language
|
|
automatically. Note that Emacs Lisp is loaded by default.
|
|
|
|
To evaluate a code block, we type Org's omnipotent =C-c C-c=. The
|
|
results will be produced below the code block. There is an optional
|
|
parameter that controls how---or even if---the results are displayed.
|
|
|
|
There are many other types of block apart from =SRC=. Those do
|
|
different things, such as:
|
|
|
|
- =#+BEGIN_QUOTE= :: Treat the contents as a block quote or equivalent.
|
|
- =#+BEGIN_VERSE= :: Do not reflow any like breaks (for poetry and such).
|
|
- =#+BEGIN_EXPORT= :: Evaluate the code for the given export target
|
|
(like =html= or =latex=), optionally replacing it with its results
|
|
or keeping both of them ([[#h:bd11d4d8-6e9f-4536-87a4-4018783bf8f5][The =unravel-org.el= Org export settings]]).
|
|
|
|
This is a wonderful world of possibilities!
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; code blocks
|
|
(use-package org
|
|
:ensure nil
|
|
:config
|
|
(setq org-confirm-babel-evaluate nil)
|
|
(setq org-src-window-setup 'current-window)
|
|
(setq org-edit-src-persistent-message nil)
|
|
(setq org-src-fontify-natively t)
|
|
(setq org-src-preserve-indentation t)
|
|
(setq org-src-tab-acts-natively t)
|
|
(setq org-edit-src-content-indentation 0))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org export settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:bd11d4d8-6e9f-4536-87a4-4018783bf8f5
|
|
:END:
|
|
|
|
Org is a capable authoring tool in no small part because it can be
|
|
converted to other file formats. A typical example is to write a
|
|
technical document in Org and then export it to a PDF. Another
|
|
use-case is what I commonly do with the Emacs packages I maintain,
|
|
which I export to an Info manual (texinfo format) and an HTML web
|
|
page.
|
|
|
|
The default set of export targets is specified in the value of the
|
|
user option ~org-export-backends~. It is one of those rare cases where
|
|
it has to be evaluated before the package is loaded. Other than that,
|
|
we can load an export backend by finding the correspond =ox-FORMAT.el=
|
|
file and either ~require~ it or load it with ~use-package~, like what
|
|
I showed for Org Babel ([[#h:1f5a0d46-5202-48dd-8048-b48ce17f3df8][The =unravel-org.el= Org code block settings]]).
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; export
|
|
(use-package org
|
|
:ensure nil
|
|
:init
|
|
;; NOTE 2023-05-20: Must be evaluated before Org is loaded,
|
|
;; otherwise we have to use the Custom UI. No thanks!
|
|
(setq org-export-backends '(html texinfo md))
|
|
:config
|
|
(setq org-export-with-toc t)
|
|
(setq org-export-headline-levels 8)
|
|
(setq org-export-dispatch-use-expert-ui nil)
|
|
(setq org-html-htmlize-output-type nil)
|
|
(setq org-html-head-include-default-style nil)
|
|
(setq org-html-head-include-scripts nil))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org capture templates (~org-capture~)
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:f8f06938-0dfe-45c3-b4cf-996d36cba82d
|
|
:END:
|
|
|
|
The ~org-capture~ command allows us to quickly store data in some
|
|
structured way. This is done with the help of a templating system
|
|
where we can, for example, record the date the entry was recorded,
|
|
prompt for user input, automatically use the email's subject as the
|
|
title of the task, and the like. The documentation string of
|
|
~org-capture-templates~ covers the technicalities.
|
|
|
|
I use two Org files for my tasks. The one is =tasks.org=, which
|
|
contains the bulk of my entries. The other is =coach.org=, which is
|
|
specific to my coaching work: https://protesilaos.com/coach.
|
|
|
|
The =tasks.org= consists of several top-level headings. Each contains
|
|
subheadings I need to review. You will notice how most of my
|
|
entries in ~org-capture-templates~ involve this file. With Org, it is
|
|
perfectly fine to work in a single file because we can fold headings
|
|
or narrow to them with ~org-narrow-to-subtree~. Furthermore, we can
|
|
navigate directly to a heading using minibuffer completion, such as
|
|
with the general purpose command ~prot-search-outline~
|
|
([[#h:b902e6a3-cdd2-420f-bc99-3d973c37cd20][The =unravel-search.el= extras provided by the =prot-search.el= library]]).
|
|
|
|
Despite the fact that Org copes well with large files, I still choose
|
|
to keep my coaching work in a separate file as a contingency plan.
|
|
Because =coach.org= includes information about appointments, I need to
|
|
be able to read it with ease from anywhere. This includes different
|
|
types of hardware, but also any kind of generic text editor or
|
|
terminal pager. I do not want to depend on features like folding,
|
|
narrowing, and the like, in times when something has gone awry.
|
|
Granted, this has never happened, though the idea makes sense.
|
|
Besides, two files are not hard to manage in this case. The
|
|
=coach.org= has a simple structure: each appointment is stored as a
|
|
top-level heading.
|
|
|
|
As for my workflow, here is an overview:
|
|
|
|
- When I want to capture data that I am not yet sure about, I add it
|
|
to the =tasks.org= "Unprocessed" heading. I periodically review
|
|
those to decide if I want to do something with them or not. If I do
|
|
not want them, I delete them. Otherwise, I file them under another
|
|
heading in the same file using the ~org-refile~ command ([[#h:024dd541-0061-4a10-b10b-b17dcd4794b9][The =unravel-org.el= Org to-do and refile settings]]).
|
|
Not everything goes into the "Unprocessed" headings, as I often
|
|
known in advance what an item is about. This is just a fallback for
|
|
those cases when I need more information to decide on the
|
|
appropriate action.
|
|
|
|
- Tasks that have an inherent time component are given a =SCHEDULED=
|
|
or =DEADLINE= timestamp (set those on demand with the commands
|
|
~org-schedule~ and ~org-deadline~, respectively). These are the only
|
|
tasks I want to see on my daily agenda ([[#h:7fe87b83-2815-4617-a5f9-d3417dd9d248][The =unravel-org.el= Org agenda settings]]).
|
|
The difference between =SCHEDULED= and =DEADLINE= is that the former
|
|
has no strict start or end time and so is flexible, while the latter
|
|
is more rigid. For example, "visit the vet today" does not have a
|
|
strict time associated with it because the doctor often deals with
|
|
emergency situations and thus their agenda is fluid. While a
|
|
coaching session of mine like "work on Emacs with PERSON" has to
|
|
start at the agreed upon time.
|
|
|
|
- I do not arbitrarily assign timestamps to tasks. If something does
|
|
not have a scheduled date or a deadline, then it does not belong on
|
|
the agenda. Otherwise, those arbitrarily defined "events" accumulate
|
|
in the agenda and crowd out the actual time-sensitive tasks. As a
|
|
result, the cognitive load is heavier and things will not be done.
|
|
So when I want to do something at some point, but have no specific
|
|
plan for it, I add is to the =tasks.org= "Wishlist". When I have
|
|
free time, I review my wishlist and pick something to work on from
|
|
there depending on my available time and mood. This keeps my
|
|
workflow both focused and stress-free.
|
|
|
|
- Finally, my =coach.org= only has time-sensitive appointments with a
|
|
=DEADLINE= associated with them. I organise the rest of my
|
|
activities in the given day based on those.
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; capture
|
|
(use-package org-capture
|
|
:ensure nil
|
|
:bind ("C-c c" . org-capture)
|
|
:config
|
|
(require 'prot-org)
|
|
|
|
(setq org-capture-templates
|
|
`(("u" "Unprocessed" entry
|
|
(file+headline "tasks.org" "Unprocessed")
|
|
,(concat "* %^{Title}\n"
|
|
":PROPERTIES:\n"
|
|
":CAPTURED: %U\n"
|
|
":END:\n\n"
|
|
"%a\n%i%?")
|
|
:empty-lines-after 1)
|
|
;; ("e" "Email note (unprocessed)" entry ; Also see `org-capture-templates-contexts'
|
|
;; (file+headline "tasks.org" "Unprocessed")
|
|
;; ,(concat "* TODO %:subject :mail:\n"
|
|
;; ":PROPERTIES:\n"
|
|
;; ":CAPTURED: %U\n"
|
|
;; ":END:\n\n"
|
|
;; "%a\n%i%?")
|
|
;; :empty-lines-after 1)
|
|
("w" "Add to the wishlist (may do some day)" entry
|
|
(file+headline "tasks.org" "Wishlist")
|
|
,(concat "* %^{Title}\n"
|
|
":PROPERTIES:\n"
|
|
":CAPTURED: %U\n"
|
|
":END:\n\n"
|
|
"%a%?")
|
|
:empty-lines-after 1)
|
|
("c" "Clock in and do immediately" entry
|
|
(file+headline "tasks.org" "Clocked tasks")
|
|
,(concat "* TODO %^{Title}\n"
|
|
":PROPERTIES:\n"
|
|
":EFFORT: %^{Effort estimate in minutes|5|10|15|30|45|60|90|120}\n"
|
|
":END:\n\n"
|
|
"%a\n")
|
|
:prepend t
|
|
:clock-in t
|
|
:clock-keep t
|
|
:immediate-finish t
|
|
:empty-lines-after 1)
|
|
("t" "Time-sensitive task" entry
|
|
(file+headline "tasks.org" "Tasks with a date")
|
|
,(concat "* TODO %^{Title} %^g\n"
|
|
"%^{How time sensitive it is||SCHEDULED|DEADLINE}: %^t\n"
|
|
":PROPERTIES:\n"
|
|
":CAPTURED: %U\n"
|
|
":END:\n\n"
|
|
"%a%?")
|
|
:empty-lines-after 1)
|
|
("p" "Private lesson or service" entry
|
|
(file "coach.org")
|
|
#'prot-org-capture-coach
|
|
:prepend t
|
|
:empty-lines 1)
|
|
("P" "Private service clocked" entry
|
|
(file+headline "coach.org" "Clocked services")
|
|
#'prot-org-capture-coach-clock
|
|
:prepend t
|
|
:clock-in t
|
|
:clock-keep t
|
|
:immediate-finish t
|
|
:empty-lines 1)))
|
|
|
|
;; NOTE 2024-11-10: I realised that I was not using this enough, so
|
|
;; I decided to simplify my setup. Keeping it here, in case I need
|
|
;; it again.
|
|
|
|
;; (setq org-capture-templates-contexts
|
|
;; '(("e" ((in-mode . "notmuch-search-mode")
|
|
;; (in-mode . "notmuch-show-mode")
|
|
;; (in-mode . "notmuch-tree-mode")))))
|
|
)
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= Org agenda settings
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:7fe87b83-2815-4617-a5f9-d3417dd9d248
|
|
:END:
|
|
|
|
[ Watch: [[https://protesilaos.com/codelog/2021-12-09-emacs-org-block-agenda/][Demo of my custom Org block agenda]] (2021-12-09). It has
|
|
changed a bit since then, but the idea is the same. ]
|
|
|
|
With the Org agenda, we can visualise the tasks we have collected in
|
|
our Org files or, more specifically, in the list of files specified in
|
|
the user option ~org-agenda-files~. In my workflow, only the files in
|
|
the ~org-directory~ can feed data into the agenda. Though Org provides
|
|
commands to add/remove the current file on demand: ~org-remove-file~,
|
|
and ~org-agenda-file-to-front~. If I ever need to write a task that is
|
|
specific to a certain file or buffer, then I use Org's linking
|
|
mechanism to point to the relevant context, but otherwise store my
|
|
task in the usual place ([[#h:f8f06938-0dfe-45c3-b4cf-996d36cba82d][The =unravel-org.el= Org capture templates (~org-capture~)]]).
|
|
|
|
By default, Org provides many so-called "views" for the agenda. One of
|
|
the them is the daily/weekly agenda. Others show only the headings
|
|
with =TODO= keywords, or some other kind of search criteria. I
|
|
personally never use those views. I have my own custom agenda view,
|
|
which consolidates in a single buffer the following blocks on data, in
|
|
this order ([[#h:9974eac8-2167-45c4-90e0-12dd877403da][The =prot-org.el= library]]).:
|
|
|
|
- Important tasks without a date :: When I add a top priority to
|
|
something, but there is no inherent deadline to it.
|
|
|
|
- Pending scheduled tasks :: Tasks with a =SCHEDULED= date may
|
|
sometimes not be done when they ought to. So they need to be closer
|
|
to the top for me to do them as soon as I can.
|
|
|
|
- Today's agenda :: What I am actually working on. Because I only
|
|
assign a timestamp to tasks that are indeed time-sensitive, this
|
|
always reflects the commitments I have for the day.
|
|
|
|
- Next three days :: Like the above, but for the near future.
|
|
|
|
- Upcoming deadlines (+14d) :: These are the deadlines I need to be
|
|
aware of for the 14 days after the next three days I am only
|
|
informed about.
|
|
|
|
The Org agenda has lots of other extras, such as to filter the view.
|
|
Though I never use them. My custom agenda does exactly what I need
|
|
from it and thus keeps me focused.
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
;;;; agenda
|
|
(use-package org-agenda
|
|
:ensure nil
|
|
:bind
|
|
;; I bind `org-agenda' to C-c A, so this one puts me straight into my
|
|
;; custom block agenda.
|
|
( :map global-map
|
|
("C-c A" . org-agenda)
|
|
("C-c a" . (lambda ()
|
|
"Call Org agenda with `prot-org-custom-daily-agenda' configuration."
|
|
(interactive)
|
|
(org-agenda nil "A"))))
|
|
:config
|
|
;;;;; Custom agenda blocks
|
|
|
|
(setq org-agenda-format-date #'prot-org-agenda-format-date-aligned)
|
|
|
|
;; Check the variable `prot-org-custom-daily-agenda' in prot-org.el
|
|
(setq org-agenda-custom-commands
|
|
`(("A" "Daily agenda and top priority tasks"
|
|
,prot-org-custom-daily-agenda
|
|
((org-agenda-fontify-priorities nil)
|
|
(org-agenda-prefix-format " %t %s")
|
|
(org-agenda-dim-blocked-tasks nil)))
|
|
("P" "Plain text daily agenda and top priorities"
|
|
,prot-org-custom-daily-agenda
|
|
((org-agenda-with-colors nil)
|
|
(org-agenda-prefix-format "%t %s")
|
|
(org-agenda-current-time-string ,(car (last org-agenda-time-grid)))
|
|
(org-agenda-fontify-priorities nil)
|
|
(org-agenda-remove-tags t))
|
|
("agenda.txt"))))
|
|
|
|
;;;;; Basic agenda setup
|
|
(setq org-default-notes-file (make-temp-file "emacs-org-notes-")) ; send it to oblivion
|
|
(setq org-agenda-files `(,org-directory))
|
|
(setq org-agenda-span 'week)
|
|
(setq org-agenda-start-on-weekday 1) ; Monday
|
|
(setq org-agenda-confirm-kill t)
|
|
(setq org-agenda-show-all-dates t)
|
|
(setq org-agenda-show-outline-path nil)
|
|
(setq org-agenda-window-setup 'current-window)
|
|
(setq org-agenda-skip-comment-trees t)
|
|
(setq org-agenda-menu-show-matcher t)
|
|
(setq org-agenda-menu-two-columns nil)
|
|
(setq org-agenda-sticky nil)
|
|
(setq org-agenda-custom-commands-contexts nil)
|
|
(setq org-agenda-max-entries nil)
|
|
(setq org-agenda-max-todos nil)
|
|
(setq org-agenda-max-tags nil)
|
|
(setq org-agenda-max-effort nil)
|
|
|
|
;;;;; General agenda view options
|
|
;; NOTE 2021-12-07: Check further below my `org-agenda-custom-commands'
|
|
(setq org-agenda-prefix-format
|
|
'((agenda . " %i %-12:c%?-12t% s")
|
|
(todo . " %i %-12:c")
|
|
(tags . " %i %-12:c")
|
|
(search . " %i %-12:c")))
|
|
(setq org-agenda-sorting-strategy
|
|
'(((agenda habit-down time-up priority-down category-keep)
|
|
(todo priority-down category-keep)
|
|
(tags priority-down category-keep)
|
|
(search category-keep))))
|
|
(setq org-agenda-breadcrumbs-separator "->")
|
|
(setq org-agenda-todo-keyword-format "%-1s")
|
|
(setq org-agenda-fontify-priorities 'cookies)
|
|
(setq org-agenda-category-icon-alist nil)
|
|
(setq org-agenda-remove-times-when-in-prefix nil)
|
|
(setq org-agenda-remove-timeranges-from-blocks nil)
|
|
(setq org-agenda-compact-blocks nil)
|
|
(setq org-agenda-block-separator ?—)
|
|
|
|
;;;;; Agenda marks
|
|
(setq org-agenda-bulk-mark-char "#")
|
|
(setq org-agenda-persistent-marks nil)
|
|
|
|
;;;;; Agenda diary entries
|
|
(setq org-agenda-insert-diary-strategy 'date-tree)
|
|
(setq org-agenda-insert-diary-extract-time nil)
|
|
(setq org-agenda-include-diary nil)
|
|
;; I do not want the diary, but there is no way to disable it
|
|
;; altogether. This creates a diary file in the /tmp directory.
|
|
(setq diary-file (make-temp-file "emacs-diary-"))
|
|
(setq org-agenda-diary-file 'diary-file) ; TODO 2023-05-20: review Org diary substitute
|
|
|
|
;;;;; Agenda follow mode
|
|
(setq org-agenda-start-with-follow-mode nil)
|
|
(setq org-agenda-follow-indirect t)
|
|
|
|
;;;;; Agenda multi-item tasks
|
|
(setq org-agenda-dim-blocked-tasks t)
|
|
(setq org-agenda-todo-list-sublevels t)
|
|
|
|
;;;;; Agenda filters and restricted views
|
|
(setq org-agenda-persistent-filter nil)
|
|
(setq org-agenda-restriction-lock-highlight-subtree t)
|
|
|
|
;;;;; Agenda items with deadline and scheduled timestamps
|
|
(setq org-agenda-include-deadlines t)
|
|
(setq org-deadline-warning-days 0)
|
|
(setq org-agenda-skip-scheduled-if-done nil)
|
|
(setq org-agenda-skip-scheduled-if-deadline-is-shown t)
|
|
(setq org-agenda-skip-timestamp-if-deadline-is-shown t)
|
|
(setq org-agenda-skip-deadline-if-done nil)
|
|
(setq org-agenda-skip-deadline-prewarning-if-scheduled 1)
|
|
(setq org-agenda-skip-scheduled-delay-if-deadline nil)
|
|
(setq org-agenda-skip-additional-timestamps-same-entry nil)
|
|
(setq org-agenda-skip-timestamp-if-done nil)
|
|
(setq org-agenda-search-headline-for-time nil)
|
|
(setq org-scheduled-past-days 365)
|
|
(setq org-deadline-past-days 365)
|
|
(setq org-agenda-move-date-from-past-immediately-to-today t)
|
|
(setq org-agenda-show-future-repeats t)
|
|
(setq org-agenda-prefer-last-repeat nil)
|
|
(setq org-agenda-timerange-leaders
|
|
'("" "(%d/%d): "))
|
|
(setq org-agenda-scheduled-leaders
|
|
'("Scheduled: " "Sched.%2dx: "))
|
|
(setq org-agenda-inactive-leader "[")
|
|
(setq org-agenda-deadline-leaders
|
|
'("Deadline: " "In %3d d.: " "%2d d. ago: "))
|
|
;; Time grid
|
|
(setq org-agenda-time-leading-zero t)
|
|
(setq org-agenda-timegrid-use-ampm nil)
|
|
(setq org-agenda-use-time-grid t)
|
|
(setq org-agenda-show-current-time-in-grid t)
|
|
(setq org-agenda-current-time-string (concat "Now " (make-string 70 ?.)))
|
|
(setq org-agenda-time-grid
|
|
'((daily today require-timed)
|
|
( 0500 0600 0700 0800 0900 1000
|
|
1100 1200 1300 1400 1500 1600
|
|
1700 1800 1900 2000 2100 2200)
|
|
"" ""))
|
|
(setq org-agenda-default-appointment-duration nil)
|
|
|
|
;;;;; Agenda global to-do list
|
|
(setq org-agenda-todo-ignore-with-date t)
|
|
(setq org-agenda-todo-ignore-timestamp t)
|
|
(setq org-agenda-todo-ignore-scheduled t)
|
|
(setq org-agenda-todo-ignore-deadlines t)
|
|
(setq org-agenda-todo-ignore-time-comparison-use-seconds t)
|
|
(setq org-agenda-tags-todo-honor-ignore-options nil)
|
|
|
|
;;;;; Agenda tagged items
|
|
(setq org-agenda-show-inherited-tags t)
|
|
(setq org-agenda-use-tag-inheritance
|
|
'(todo search agenda))
|
|
(setq org-agenda-hide-tags-regexp nil)
|
|
(setq org-agenda-remove-tags nil)
|
|
(setq org-agenda-tags-column -100)
|
|
|
|
;;;;; Agenda entry
|
|
;; NOTE: I do not use this right now. Leaving everything to its
|
|
;; default value.
|
|
(setq org-agenda-start-with-entry-text-mode nil)
|
|
(setq org-agenda-entry-text-maxlines 5)
|
|
(setq org-agenda-entry-text-exclude-regexps nil)
|
|
(setq org-agenda-entry-text-leaders " > ")
|
|
|
|
;;;;; Agenda logging and clocking
|
|
;; NOTE: I do not use these yet, though I plan to. Leaving everything
|
|
;; to its default value for the time being.
|
|
(setq org-agenda-log-mode-items '(closed clock))
|
|
(setq org-agenda-clock-consistency-checks
|
|
'((:max-duration "10:00" :min-duration 0 :max-gap "0:05" :gap-ok-around
|
|
("4:00")
|
|
:default-face ; This should definitely be reviewed
|
|
((:background "DarkRed")
|
|
(:foreground "white"))
|
|
:overlap-face nil :gap-face nil :no-end-time-face nil
|
|
:long-face nil :short-face nil)))
|
|
(setq org-agenda-log-mode-add-notes t)
|
|
(setq org-agenda-start-with-log-mode nil)
|
|
(setq org-agenda-start-with-clockreport-mode nil)
|
|
(setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel 2))
|
|
(setq org-agenda-search-view-always-boolean nil)
|
|
(setq org-agenda-search-view-force-full-words nil)
|
|
(setq org-agenda-search-view-max-outline-level 0)
|
|
(setq org-agenda-search-headline-for-time t)
|
|
(setq org-agenda-use-time-grid t)
|
|
(setq org-agenda-cmp-user-defined nil)
|
|
(setq org-agenda-sort-notime-is-late t) ; Org 9.4
|
|
(setq org-agenda-sort-noeffort-is-high t) ; Org 9.4
|
|
|
|
;;;;; Agenda column view
|
|
;; NOTE I do not use these, but may need them in the future.
|
|
(setq org-agenda-view-columns-initially nil)
|
|
(setq org-agenda-columns-show-summaries t)
|
|
(setq org-agenda-columns-compute-summary-properties t)
|
|
(setq org-agenda-columns-add-appointments-to-effort-sum nil)
|
|
(setq org-agenda-auto-exclude-function nil)
|
|
(setq org-agenda-bulk-custom-functions nil)
|
|
|
|
;; ;;;;; Agenda habits
|
|
;; (require 'org-habit)
|
|
;; (setq org-habit-graph-column 50)
|
|
;; (setq org-habit-preceding-days 9)
|
|
;; ;; Always show the habit graph, even if there are no habits for
|
|
;; ;; today.
|
|
;; (setq org-habit-show-all-today t)
|
|
)
|
|
#+end_src
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
(use-package prot-coach
|
|
:ensure nil
|
|
:commands (prot-coach-done-sessions-with-person))
|
|
#+end_src
|
|
|
|
*** The =unravel-org.el= call to ~provide~
|
|
:PROPERTIES:
|
|
:CUSTOM_ID: h:62eb7ca3-2f79-45a6-a018-38238b486e98
|
|
:END:
|
|
|
|
Finally, we ~provide~ the module. This is the mirror function of
|
|
~require~ ([[#h:e6c4acf5-5b51-4b38-a86a-bf3f698ac872][The init.el final part to load the individual modules]]).
|
|
|
|
#+begin_src emacs-lisp :tangle "unravel-modules/unravel-org.el"
|
|
(provide 'unravel-org)
|
|
#+end_src
|
|
|