Victor Dorneanu has an interesting post on Cybernetic Productivity and Emacs. Like Dorneanu, I was unfamiliar with the term but it has, apparently, been around since the early 2000s. It’s a strategy for dealing with your workload in the digital age. The TL;DR is summed up by its four principals:
Automate and Speedup shallow tasks.
Keep the information you need at your fingertips.
Remove friction from communication.
Simplify the extraction of actionable information from raw data
Dorneanu notes that he learned all this from Cal Newport, whose work he follows fairly closely. Newport, though, says the cybernetic productivity approach doesn’t work because, if I understand his argument, we’re too overwhelmed with work to have time to follow the principals.
Dorneanu disagrees and offers his use of Emacs as a counterargument. His post details how he used the four principals—except, maybe, the communication part—to write and publish his current post. The important part is that he did it all within the unified environment of Emacs. He lets Emacs handle the routine parts of publishing a post and uses Elfeed to easily retrieve the necessary required information.
Finally, he notes that the recent integration of Emacs with the various AI/ML services provides a very nice and efficient way of sifting through data looking for the wheat among the chaff. I’d judge that more promise than fact right now but I do take his point.
Summary:
In this post, I share Errant’s Chase procedure. I then write out the mathematics of calculating the movement dice. And from there provide an Emacs function for calculating movement dice. The chase subsystem is too interesting to ignore the movement dice. The underlying question is “should we care about the movement dice in combat?” I think so, because those dice add to the unpredictability of the game.
I was reading Errant📖
and got curious about the mathematic behind the fiddly Movement Dice. A character’s Movement Dice are derived from their physique, skill, and number of item Slots carried.
The Movement Dice is a proud and odd little rule that impacts movement in combat as well as chases. My conjecture is that the chase mechanic encourages you to throw down your backpack and run; multiple dice are advantageous for escaping.
Of important note, the only time a character’s movement dice matter is during a chase and during combat. Exploration is not based on the movement dice.
Errant’s Chase Procedure
First let’s look at the Chases section of Errant
:
In the case where they are being chased through a dungeon or similarly defined area, or for a short pursuit, the hunt can play out using standard initiative turn rules. However, for longer pursuits, and ones that may take place in broadly abstracted spaces like the wilderness or in cities, the following chase procedure can be used.
In a chase, generally, the participants can be tracked in terms of what side they’re on (i.e. pursuers and fugitives), but some chases may involve multiple parties or characters that need to be tracked separately.
Each initiative turn, the character with the lowest spd or mv on each side makes a movement roll. If the characters are on mounts or vehicles, use the spd of the mount or vehicle.
If the fugitives roll two 4’s, then they escape and the chase ends. If the pursuers roll two 4’s, they have caught the fugitives. In case of a tie, both sides make a movement roll as a tiebreaker.
If either side rolls doubles that are not 4’s, then characters on that side may make melee attack roll, perform a sorcery or miracle, or any other actions they wish.
If any of the results on both side’s movement dice match each other, characters may make ranged attack rolls against the other side. So if the fugitives rolled a 3 and a 4, and the pursuers rolled a 2 and a 3, the 3’s match, and so characters on each side may make ranged attack rolls against characters on the other side.
Dropping items during a chase is a free action. Dropping something the pursuers are interested in (food, money, etc.) may force a morale roll to see if they continue the chase.
Characters on either side may choose to sprint, rolling double their normal amount of movement dice, but they must make a physcheck with a dv equal to their encumbrance to do so. If they fail, they may not make a movement roll this initiative turn.
Characters on either side can choose to split off from their group; they will make movement rolls separately.
At the end of the initiative turn, if the chase has not yet ended, the side that rolled the lowest on their movement roll rolls a d10 for a chasedevelopment that affects them.
We have some high stakes dice rolls where having more movement dice increases your chances of ending the chase or taking meaningful actions during the chase. There’s a press your luck element in choosing to sprint (and garner potentially more dice). There’s also the option to split from the slower characters.
All modeling the absolute chaos of a good old fashioned rout.
On to the Mathematic of It All
With that in mind, we dive into the fiddly bits. We use two attributes (e.g.physique and skill) plus the more fluid number of carried Slots.
Errant
encourages you to calculate your Spd while carrying your backpack and when not carrying your backpack.
The book wants to make it clear just how much that backpack is costing you during a chase.
A character’s ENC (or Encumbrance) is:
When slots > physique, then 4 + slots - physique
Else (4 × slots) ÷ physique rounded down.
A character’s SPD (or Speed) is skill - encumberance. And their movement die is speed + 4 rounded down.
Emacs Lisp Function
The following function prompts for information and provides movement dice while carrying your backpack and when not carrying your backpack.
I suppose it would also be useful to see the effect of discarding things carried in your hands. Because when you’re running maybe discarding that greatsword is a better plan than carrying the dead weight.
I like the chase procedure; and the fiddly bits make the procedure possible. Also given that combat might lead to chase, having a clear understanding of a character’s movement dice helps folks think about the viability of fleeing.
If I don’t want to worry about it, a quick and dirty method could be:
Pick the higher of physique or skill and subtract the slots.
Select the lowest sided-die from the dice chain that has sides greater than the above amount.
James Dyer has an interesting post on using Gnuplot to plot data from Org tables. Gnuplot is my go to utility for plotting data but its syntax is, to put it mildly, convoluted and since I don’t use it all that often, it’s always a struggle to get a working graph. Dyer’s method seems less complicated—at least for simple cases—so it’s worth taking note of how he did it.
This post is mainly a note to myself but other occasional Gnuplot users who want to produce graphs from the data in an Org table may find it useful too. The TL;DR is that you specify a #+PLOT: line for the table that lists the parameters for the graph you wish to plot.
As Dyer illustrates, you can also easily produce a bar plot within the table itself with the orgtbl-ascii-draw command. It’s use is pretty much self explanatory from Dyer’s post but it’s also documented at the #+PLOT link.
Dyer’s post is short but has a surprisingly useful kernel. It’s definitely worth taking a look at it if you ever need to plot data contained in an Org table.
I’m currently in the process of learning how to create graphical plots from org tables using gnuplot. I’ve noticed that it’s generally more straightforward to extract x-axis data from an org table column with incrementing numbers, as opposed to relying on gnuplot to potentially sort out data from an existing column (which may not be plot-friendly)
Generally my existing org tables do not have such an incrementing integer column, so how do I quickly create and populate such a column?, well actually it is super easy, barely an inconvenience!
For example take the following table that represents a little flutter on the horses, how would I go about plotting the Total column and therefore clearly visualise the downward sloping line 😀
The EmacsConf 2023 call for participation has finished, hooray! We've
sent out acceptances and added talks to the wiki. We experimented with
early acceptances this year, which was really nice because it gives
people quick feedback and allows people to get started on their videos
early. That meant that I needed to be able to easily add talks to the
wiki throughout the call for participation. We use templates and an
Ikiwiki directive to make it easier to consistently format talk pages.
This post covers adding a talk to the wiki, and these functions are in
the emacsconf-el repository.
Amin Bandali picked Ikiwiki for the wiki for emacsconf.org. I think
it's because Ikiwiki works with plain text in a Git repository, which
fits nicely with our workflow. I can use Emacs Lisp to generate files
that are included in other files, and I can also use Emacs Lisp to
generate the starting point for files that may be manually edited
later on.
We organize conference pages by year. Under the directory for this
year (2023/), we have:
talks
talk descriptions that can be manually edited
talks/SLUG.md
the description for the talk. Includes
../info/SLUG-nav.md, ../info/SLUG-before.md, and
../info/SLUG-after.md
info
automatically-generated files that are included before and
after the talk description, and navigation links between talks
info/SLUG-nav.md
navigation links
info/SLUG-before.md
navigation links
info/SLUG-after.md
navigation links
schedule-details.md
list of talks
organizers-notebook/index.org
public planning notebook
The filenames and URLs for each talk are based on the ID for a talk. I
store that in the SLUG property to make it easy to differentiate
from CUSTOM_ID, since CUSTOM_ID is useful for lots of other things
in Org Mode. I usually assign the slugs when I add the talks to our
private conf.org file, although sometimes people suggest a specific
ID.
Templating
Publishing to wiki pages and replying to e-mails are easier if I can
substitute text into readable templates. There are a number of
templating functions for Emacs Lisp, like the built-in tempo.el or
s-lex-format from s.el. I ended up writing something that works
with plists instead, since we use property lists (plists) all over the
emacsconf-el library.
emacsconf-replace-plist-in-string: Replace ${keyword} from ATTRS in STRING.
(defunemacsconf-replace-plist-in-string (attrs string)
"Replace ${keyword} from ATTRS in STRING."
(let ((a attrs) name val)
(while a
(setq name (pop a) val (pop a))
(when (stringp val)
(setq string
(replace-regexp-in-string (regexp-quote (concat "${" (substring (symbol-name name) 1) "}"))
(or val "")
string t t))))
string))
It is also handy to be able to add text around another string only if
the string is non-nil, and to provide a different string to use if it
isn't specified..
emacsconf-surround: Concat BEFORE, TEXT, and AFTER if TEXT is specified, or return ALTERNATIVE.
(defunemacsconf-surround (before text after &optional alternative)
"Concat BEFORE, TEXT, and AFTER if TEXT is specified, or return ALTERNATIVE."
(if (and text (not (string= text "")))
(concat (or before "") text (or after ""))
alternative))
Getting the talk information
To get the data to fill in the template, we can run a bunch of
different functions. This lets us add or remove functions when we need
to. We pass the previous result to the next function in order to
accumulate properties or modify them. The result is a property list
for the current talk.
emacsconf-get-talk-info-for-subtree: Run ‘emacsconf-talk-info-functions’ to extract the info for this entry.
(defunemacsconf-get-talk-info-for-subtree ()
"Run `emacsconf-talk-info-functions' to extract the info for this entry."
(seq-reduce (lambda (prev val) (save-excursion (save-restriction (funcall val prev))))
emacsconf-talk-info-functions
nil))
emacsconf-talk-info-functions: Functions to collect information.
I add a *** Talk abstract subheading to the talk and put the rest of
the submission under a *** Talk details subheading. This allows me
to extract the text of the Talk abstract heading (or whatever
matches emacsconf-abstract-heading-regexp, which is set to "abstract".).
emacsconf-get-subtree-entry: Return the text for the subtree matching HEADING-REGEXP.
(defunemacsconf-get-subtree-entry (heading-regexp)
"Return the text for the subtree matching HEADING-REGEXP."
(car
(delq nil
(org-map-entries
(lambda ()
(when (string-match heading-regexp (org-entry-get (point) "ITEM"))
(org-get-entry)))
nil 'tree))))
emacsconf-get-talk-abstract-from-subtree: Add the abstract from a subheading.
(defunemacsconf-get-talk-abstract-from-subtree (o)
"Add the abstract from a subheading.The subheading should match `emacsconf-abstract-heading-regexp'."
(plist-put o :abstract (substring-no-properties (or (emacsconf-get-subtree-entry "abstract") ""))))
I include emacsconf-get-talk-abstract-from-subtree in
emacsconf-talk-info-functions so that it retrieves that information
when I call emacsconf-get-talk-info-for-subtree.
Publishing the talk page
We add accepted talks to the wiki so that people can see what kinds of
talks will be at EmacsConf 2023. To add the talk to the wiki, I use
emacsconf-publish-add-talk. It'll create the talk page without
overwriting anything that's already there and redo the
automatically-generated info pages that provide navigation, status,
and so on.
emacsconf-publish-add-talk: Add the current talk to the wiki.
(defunemacsconf-publish-add-talk ()
"Add the current talk to the wiki."
(interactive)
(emacsconf-publish-talk-page (emacsconf-get-talk-info-for-subtree))
(emacsconf-publish-info-pages)
(magit-status-setup-buffer emacsconf-directory))
The talk page includes the description and other resources.
emacsconf-publish-talk-page: Draft the talk page for O unless the page already exists or FORCE is non-nil.
Ikiwiki uses Markdown, so we can take advantage of Org's Markdown export.
emacsconf-convert-talk-abstract-to-markdown: Set the :abstract-md property to a Markdown version of the abstract.
(defunemacsconf-convert-talk-abstract-to-markdown (o)
"Set the :abstract-md property to a Markdown version of the abstract."
(plist-put o :abstract-md (org-export-string-as (or (plist-get o :abstract) "") 'md t)))
The emacsconf-format-main-schedule function displays the information
for the talks in each track. It's pretty straightforward, but I put it
in a function because I call it from a number of places.
emacsconf-publish-format-main-schedule: Include the schedule information for INFO.
(defunemacsconf-publish-format-main-schedule (info)
"Include the schedule information for INFO."
(mapconcat #'emacsconf-publish-sched-directive info "\n"))
We define an Ikiwiki sched directive that conditionally displays
things depending on what we specify, so it's easy to add more
information during the schedule or conference phase. This is
templates/sched.md in the EmacsConf wiki git repository:
This Emacs Lisp function converts a talk into that directive.
emacsconf-publish-sched-directive: Format the schedule directive with info for O.
(defunemacsconf-publish-sched-directive (o)
"Format the schedule directive with info for O."
(format "[[!template id=sched%s]]"
(let ((result "")
(attrs (append
(pcase emacsconf-publishing-phase
('program
(list
:time (plist-get o :time)))
((or'schedule'conference)
(list
:status (pcase (plist-get o :status)
("CAPTIONED""captioned")
("PREREC_RECEIVED""received")
("DONE""done")
("STARTED""now playing")
(_ nil))
:time (plist-get o :time)
:q-and-a (plist-get o :qa-link)
:pad (and emacsconf-publish-include-pads (plist-get o :pad-url))
:startutc (format-time-string "%FT%T%z" (plist-get o :start-time) t)
:endutc (format-time-string "%FT%T%z" (plist-get o :end-time) t)
:start (format-time-string "%-l:%M" (plist-get o :start-time) emacsconf-timezone)
:end (format-time-string "%-l:%M" (plist-get o :end-time) emacsconf-timezone)))
('resources
(list
:pad nil
:channel nil
:resources (mapconcat (lambda (s) (concat "<li>" s "</li>"))
(emacsconf-link-file-formats-as-list
(append o
(list :base-url (format "%s%s/" emacsconf-media-base-url emacsconf-year)))
(append emacsconf-main-extensions (list "--answers.webm""--answers.opus""--answers.vtt")))
""))))
(list
:title (plist-get o :title)
:url (concat "/" (plist-get o :url))
:speakers (plist-get o :speakers)
:track (if (member emacsconf-publishing-phase '(schedule conference)) (plist-get o :track))
:watch (plist-get o :watch-url)
:slug (plist-get o :slug)
:note
(string-join
(delq nil
(list
(when (plist-get o :captions-edited)
"captioned")
(when (and (plist-get o :public)
(or (plist-get o :toobnix-url)
(plist-get o :video-file)))
"video posted")))
", ")
)
)))
(while attrs
(let ((field (pop attrs))
(val (pop attrs)))
(when val
(setq result (concat result " " (substring (symbol-name field) 1) "=\"\"\"" val "\"\"\"")))))
result)))
Publishing auto-generated navigation
It's nice to be able to navigate between talks without going back to
the schedule page each time. This is handled by just keeping two extra
copies of the list: one with the first talk popped off, and one with
an extra element added to the beginning. Then we can use the heads of
those lists for next/previous links.
Figure 2: Navigation
emacsconf-publish-nav-pages: Generate links to the next and previous talks.
(defunemacsconf-publish-nav-pages (&optional talks)
"Generate links to the next and previous talks.During the schedule and conference phase, the talks are sorted by time.Otherwise, they're sorted by track and then schedule."
(interactive (list (emacsconf-publish-prepare-for-display (or emacsconf-schedule-draft (emacsconf-get-talk-info)))))
(let* ((next-talks (cdr talks))
(prev-talks (cons nil talks))
(label (if (member emacsconf-publishing-phase '(schedule conference))
"time""track")))
(unless (file-directory-p (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
(mkdir (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory))))
(while talks
(let* ((o (pop talks))
(next-talk (emacsconf-format-talk-link (pop next-talks)))
(prev-talk (emacsconf-format-talk-link (pop prev-talks))))
(with-temp-file (expand-file-name (format "%s-nav.md" (plist-get o :slug))
(expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
(insert (concat "\n<div class=\"talk-nav\">Back to the [[talks]] \n"
(if prev-talk (format "Previous by %s: %s \n" label prev-talk) "")
(if next-talk (format "Next by %s: %s \n" label next-talk) "")
(if (plist-get o :track) ; tagging doesn't work here because ikiwiki will list the nav page
(format "Track: <span class=\"sched-track %s\">%s</span> \n" (plist-get o :track) (plist-get o :track))
"")
"</div>")))))))
Before the talk description
We include some details about the schedule in the talk page, before the description.
Figure 3: Description
emacsconf-publish-before-page: Generate the page that has the info included before the abstract.
(defunemacsconf-publish-before-page (talk &optional info)
"Generate the page that has the info included before the abstract.This includes the intro note, the schedule, and talk resources."
(interactive (list (emacsconf-complete-talk-info)))
(setq info (or info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info))))
(with-temp-file (expand-file-name (format "%s-before.md" (plist-get talk :slug))
(expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
(insert "<!-- Automatically generated by emacsconf-publish-before-page -->\n")
(insert (emacsconf-surround "" (plist-get talk :intro-note) "\n\n"""))
(let ((is-live (emacsconf-talk-live-p talk)))
(when is-live (emacsconf-publish-captions-in-wiki talk))
(when (member emacsconf-publishing-phase '(schedule conference))
(insert (emacsconf-publish-format-talk-schedule-image talk info)))
(insert (emacsconf-publish-format-talk-schedule-info talk) "\n\n")
(insert
(if (plist-get talk :public) (emacsconf-wiki-talk-resources talk) "")
"\n# Description\n"))
(insert "<!-- End of emacsconf-publish-before-page -->")))
emacsconf-publish-format-talk-schedule-info: Format schedule information for O.
(defunemacsconf-publish-format-talk-schedule-info (o)
"Format schedule information for O."
(let ((friendly (concat "/" emacsconf-year "/talks/" (plist-get o :slug) ))
(timestamp (org-timestamp-from-string (plist-get o :scheduled))))
(emacsconf-replace-plist-in-string
(append o
(list
:format
(concat (or (plist-get o :video-time)
(plist-get o :time))
"-min talk"
(if (plist-get o :q-and-a)
(format " followed by %s Q&A%s"
(plist-get o :q-and-a)
(if (eq emacsconf-publishing-phase 'conference)
(format " (%s)"
(if (string-match "live" (plist-get o :q-and-a))
(if (eq 'after (emacsconf-bbb-status o))
"done"
(format "<https://emacsconf.org/current/%s/room>" (plist-get o :slug)))
(emacsconf-publish-webchat-link o)))
""))
""))
:pad-info
(if emacsconf-publish-include-pads
(format "Etherpad: <https://pad.emacsconf.org/%s-%s> \n" emacsconf-year (plist-get o :slug))
"")
:irc-info
(format "Discuss on IRC: [#%s](%s) \n" (plist-get o :channel)
(plist-get o :webchat-url))
:status-info
(if (member emacsconf-publishing-phase '(cfp program schedule conference)) (format "Status: %s \n" (plist-get o :status-label)) "")
:schedule-info
(if (and (member emacsconf-publishing-phase '(schedule conference))
(not (emacsconf-talk-all-done-p o))
(not (string= (plist-get o :status) "CANCELLED")))
(let ((start (org-timestamp-to-time (org-timestamp-split-range timestamp)))
(end (org-timestamp-to-time (org-timestamp-split-range timestamp t))))
(format
"<div>Times in different timezones:</div><div class=\"times\" start=\"%s\" end=\"%s\"><div class=\"conf-time\">%s</div><div class=\"others\"><div>which is the same as:</div>%s</div></div><div><a href=\"/%s/watch/%s/\">Find out how to watch and participate</a></div>"
(format-time-string "%Y-%m-%dT%H:%M:%SZ" start t)
(format-time-string "%Y-%m-%dT%H:%M:%SZ" end t)
(emacsconf-timezone-string o emacsconf-timezone)
(string-join (emacsconf-timezone-strings
o
(seq-remove (lambda (zone) (string= emacsconf-timezone zone))
emacsconf-timezones)) "<br />")
emacsconf-year
(plist-get (emacsconf-get-track (plist-get o :track)) :id)))
"")))
(concat
"[[!toc ]]Format: ${format} ${pad-info}${irc-info}${status-info}${schedule-info}\n"
(if (plist-get o :alternate-apac)
(format "[[!inline pages=\"internal(%s/inline-alternate)\" raw=\"yes\"]] \n" emacsconf-year)
"")
"\n"))))
After the talk description
After the talk description, we include a footer that makes it easier
for people to e-mail questions using either the PUBLIC_EMAIL
property of the talk or the emacsconf-org-private e-mail address.
emacsconf-publish-format-email-questions-and-comments: Invite people to e-mail either the public contact for TALK or the private list.
(defunemacsconf-publish-format-email-questions-and-comments (talk)
"Invite people to e-mail either the public contact for TALK or the private list."
(format "Questions or comments? Please e-mail %s"
(emacsconf-publish-format-public-email talk
(or
(and (string= (plist-get talk :public-email) "t")
(plist-get talk :email))
(plist-get talk :public-email)
"emacsconf-org-private@gnu.org"))))
emacsconf-publish-after-page: Generate the page with info included after the abstract.
(defunemacsconf-publish-after-page (talk &optional info)
"Generate the page with info included after the abstract.This includes captions, contact, and an invitation to participate."
(interactive (list (emacsconf-complete-talk-info)))
;; Contact information
(with-temp-file (expand-file-name (format "%s-after.md" (plist-get talk :slug))
(expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
(insert
"<!-- Automatically generated by emacsconf-publish-after-page -->\n""\n\n";; main transcript
(if (plist-get talk :public) (emacsconf-publish-format-captions talk) "")
(emacsconf-publish-format-email-questions-and-comments talk) "\n"
(if (eq emacsconf-publishing-phase 'cfp)
(format "\n----\nGot an idea for an EmacsConf talk or session? We'd love to hear from you! Check out the [[Call for Participation|/%s/cfp]] for details.\n" emacsconf-year)
"")
"\n\n<!-- End of emacsconf-publish-after-page -->\n")))
Whenever the schedule changes
This function makes it easier to regenerate all those dynamic pages
that need to be updated whenever the schedule changes.
emacsconf-publish-info-pages: Populate year/info/*-nav, -before, and -after files.
(defunemacsconf-publish-info-pages (&optional info)
"Populate year/info/*-nav, -before, and -after files."
(interactive (list nil))
(setq info (or info (emacsconf-publish-prepare-for-display info)))
(emacsconf-publish-with-wiki-change
(emacsconf-publish-nav-pages info)
(emacsconf-publish-schedule info)
(mapc (lambda (o)
(emacsconf-publish-before-page o info)
(emacsconf-publish-after-page o info))
info)))
Summary
So once the review period has passed and we're ready to accept the
talk, I change the status to WAITING_FOR_PREREC and find room for it
in the schedule. Then I use emacsconf-publish-add-talk to add the
talk description to the wiki. I review the files it generated, tweak
hyperlinks as needed, add the pages to the Git repository, and push
the commit to the server. If I rearrange talks or change times, I just
need to run emacsconf-publish-info-pages and all the
dynamically-generated pages will be updated.
Once again, as is occasionally the happens, I’ve stumbled across an Emacs user who discovered that some Emacs keybindings work in macOS. This user, sudeenhux, realized that he could navigate in the Apple Mail app with the normal Emacs bindings. He had no idea what was going on and took to reddit to ask what he had done to enable this. When reddit told him this was built in to macOS and worked with all macOS applications, he was overjoyed.
The thing is, of course, that what sudeenhux discovered is only half the story. As I’ve reported many times before—most recently here—macOS lets you configure your own bindings for its navigation commands. That would be a pain if you had to do it piecemeal but the way it works is that you put the bindings in a file and everything happens automatically. Even better, someone has already done the hard part and produced the file so all you have to do is download it and install it in the proper place. All the details are in the above link.
As a bonus, I recently got tired of not having Ctrl+Delete work in all macOS apps so I added an entry to enable it. That shows how easy it is to add your own bindings, assuming, of course, that macOS has a command that supports it.
I’m always surprised that so many people don’t know this so I like to remind everyone once in a while. Some of you may be tired of hearing about it but to those who don’t already know, it’s a revelation.
Emacs has had long-standing features like outline-mode and
outline-minor-mode that often get forgotten in the face of newer
affordances like org-mode. At the same time, Emacs continues to
acquire new features — in this article, I am specifically alluding
to repeat-mode. The combinatorial explosion that is the result of
the various ways of bringing these together is mind-boggling — and
often the result is that one fails to take advantage of what has
become possible. The result is that one ends up sticking to one's
long-established habits at the risk of failing to significantly
enhance one's productivity.
2. Background
Structured navigation is a significant productivity enhancer —
especially in my case since I rely exclusively on an auditory interface.
The above applies to both textual documents and programming source.
Starting all the way back in 1991, I started using folding-mode
to organize code into hierarchical containers that could be
expanded and collapsed; and this is what makes Emacs' eco-system
amazing; folding-mode still works and is available on melpa in 2023.
About a year ago one of the Emacs maintainers (Stefan Mounier)
helped me update portions of Emacspeak, and in the process pointed
out that I could just use outline-minor-mode to expand and
collapse sections of code in .el files.
At the time I filed this away for later use — I was still
reluctant to abandon the 30+ year investment in folding-mode.
About a year ago, I discovered repeat-mode in Emacs and started
leveraging it for everything — including outline-mode and
org-mode amongst others.
Despite the years of investment in folding-mode, it had one
drawback; keeping the fold-marks (special comments) in sync was
always a bit of a hastle.
Bringing The Old And New Together
This week I brought all of the above context together to:
Cut over the Emacspeak codebase to stop using folding-mode.
Turning the fold-marks to comments that outline-minor-mode
understood was a trivial application of my typo.plPerl script.
I had already set up Emacspeak to use repeat-mode for the
various outline modes.
Another annoyance with outline that I had fixed over 20+ years
ago was to pick an easier to press prefix-key for outline; I use
C-o.
3. The Resulting Experience
I can now skim the Emacspeak sources (as well as the Emacs
sources of course) with very few keystrokes.
Example: Pressing C-o C-n navigates by section headings; when
skimming, I only need to press C-o the first time thanks to
repeat-mode.
I also bound j and k in the outline-mode keymaps to avoid
having to chord when skimming — j is easier to press than C-n.
4. For The Future
Would be nice to enhance outline-minor-mode to understand
sectioning comments in other programming languages.
The annoyance with the default (and unusable) prefix key for the
outline modes needs to fix in Emacs core.
_xor had an interesting idea: can we use org-protocol to link to
things inside Emacs, so that we can have a webpage with bookmarks into
our Org files? Here's a quick hack that reuses org-store-link and
org-link-open.
(defunorg-protocol-open-link (info)
"Process an org-protocol://open style url with INFO."
(org-link-open (car (org-element-parse-secondary-string (plist-get info :link) '(link)))))
(defunorg-protocol-copy-open-link (arg)
(interactive"P")
(kill-new (concat "org-protocol://open?link=" (url-hexify-string (org-store-link arg)))))
(with-eval-after-load'org
(add-to-list 'org-protocol-protocol-alist'("org-open":protocol"open":function org-protocol-open-link)))
To make exporting and following easier, we also need a little code to
handle org-protocol links inside Org.
Emacs view mode is one of things that might seem superfluous at first glance. After all, you can always just visit a file and use the normal navigation commands to view the file. But there’s a lot to be said for a read-only file viewer—think less, more, or other pager utilities—that let’s you page through the file without worrying about accidentally introducing a change.
The normal view mode protocol is to use Space and Delete to scroll forward and backward like Ctrl+v and Meta+v do. It’s a nice and convenient way of viewing a file that you don’t want to edit.
Charles Choi agrees with all that but wants a richer protocol. For instance, if he’s in an Org file, he’d like the movement to be forward and backward to the next heading. Once you make that leap, you realize that other file types could also benefit from specialized navigation commands. He has a post that explores the idea of file type naviation in view mode.
He presents some code to do this for several file types and it would be no problem to add other file types as well. Even if you don’t know Elisp, it would be easy to follow the cases he presents to add your own.
View mode is an underused feature of Emacs and Choi’s enhancements make it even better. Take a look at his post and see if you don’t agree.
After last week's embarassingly-handled WebP 0-day, I
realized my Synapse instance was sorely out of date and now had a
Pegasus-class vulnerability on it. Unfortunately, the dockerfile I had been using to manage that
service on my Wobscale server was
out of date and didn't build with more recent versions of Synapse.
Rather than using the upstream Debian-based Dockerfile, I was using one
prepared by my dear friend iliana, one which she
stopped using quite a while ago and I was maintaining myself. Welp
nyaa~.
After briefly considering migrating to spantaleev/matrix-docker-ansible-deploy,
and doing some math on exactly how much data a federating synapse node
passes in a week or a month, I decided I would move the Synapse install
on to my home network with my Wobscale 1U acting as a reverse proxy to
my homelab machine over Tailscale.
And so on Friday afternoon I decided to wreck my sleep schedule and
migrate across.
In I'm now running my
Matrix Synapse instance on my Wobserver, I've written at length
about this migration process, but I will spare the RSS feed the gruesome
details. Click through if you're curious about moving a functioning
Synapse instance to a NixOS machine with "only" 24 hours of
downtime.
Synapse was one of the last services running on my Seattle 1U server;
it was originally deployed back in like 2017 and has served me well but
it's a geriatric Fedora Linux install that
is now only running my Wallabag server on
it. Once I migrate that to The Wobserver in my living room,
I'll be able to turn this host down and ask ili for a small VM that can
do the edge networking and be managed by my NixOS ecosystem instead of
hand-tuned nginx configurations. That'll
be nice.
This simultaneously took more and less work than I expected it to,
and it's certainly not a perfect migration, but it is nice to be done
with. It took about 22 hours of downtime all said and done, including
some time spent sleeping while the thing was semi-functional.
This migration was a huge fnord for months
where I would say "i should update my synapse, ugh, i should migrate my
synapse to nixos, ugh, i should move to conduit, ugh i should just sign
up for beeper.com and never touch synapse again" every time my disk
would fill up and i would have to do some stupid bullshit to clean it up
enough to run VACUUM FULL on the synapse
DB. It's still 65 fucking GB of old events I never want to see, and I
recently learned why: "unfortunately the matrix-synapse delete room API
does not remove anything from stategroupsstate. This is
similar to the way that the matrix-synapse message retention policies
also do not remove anything from stategroupsstate." this
kills me, and this is probably why my next step will be to set up matrix-synapse-diskspace-janitor.
org-clone-subtree-with-time-shift with negative shifts This post is at the weekly Emacs Tips Reddit thread1. I use org-clone-subtree-with-time-shift (C-c C-x c on an org heading) sometimes when I need richer agenda entries than using a repeater like <2023-09-25 Mon +1w>. This allows for better notes upon individual dates, as well as more nuanced control like “except those three Mondays”. This morning I accidentally started my clones a couple weeks in the future, meaning I missed today and next week.
The video will be recorded and can be watched later. I will do
some work on my Denote package and maybe tackle other Emacs-related
issues. I plan to make this about 2 hours long, though we will see in
practice.
About 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.
Package name (GNU ELPA): denote
Official manual: https://protesilaos.com/emacs/denote
For Haskell code I can use lsp-format-buffer and lsp-format-region to keep
my file looking nice, but I've never found a function for doing the same for
Cabal files. There's a nice command line tool, cabal-fmt, for doing it, but it
means having to jump to a terminal. It would of course be nicer to satisfy my
needs for aesthetics directly from Emacs. A few times I've thought of writing
the function myself, I mean how hard can it be? But then I've forgotten about it
until then next time I'm editing a Cabal file.
A few days ago I noticed emacs-reformatter popping up in my feeds. That
removed all reasons to procrastinate. It turned out to be very easy to set up.
The package doesn't have a recipe for straight.el so it needs a :straight
section. Also, the naming of the file in the package doesn't fit the package
name, hence the slightly different name in the use-package declaration:1
in order to create functions for formatting, cabal-format-buffer and
cabal-format-region, as well as a minor mode for formatting on saving a Cabal
file.
I'm sure it's possible to use :files to deal with this, but I'm not sure
how and my naive guess failed. It's OK to be like this until I figure it out
properly.
Enhancements for the default minibuffer completion UI of Emacs. In
essence, MCT is (i) a very thin layer of interactivity on top of the
out-of-the-box completion experience, and (ii) glue code that combines
built-in functionalities to make the default completion framework work
like that of more featureful third-party options.
Backronym: Minibuffer Confines Transcended; Minibuffer and
Completions in Tandem.
Below are the release notes.
Resumption of MCT development
In April 2022, I announced that I was discontinuing the development of
my mct package. At the time, Emacs 29 was gaining new MCT-like
capabilities and I thought we would quickly reach a point where my
package would be superseded by built-in functionality. The article I
published at the time:
https://protesilaos.com/codelog/2022-04-14-emacs-discontinue-mct/.
About a year later and after receiving questions about MCT, I decided
to restart its development. This was done in light of the realisation
that the built-in Emacs functionality was still not as opinionated as
MCT. There are good reasons for this state of affairs, due to the
legacy of this important User Interface element and Emacs’ policy to
not break stuff willy nilly. Still, the fact remains that MCT can fit
in a very narrow niche for those who (i) like the built-in completions
and (ii) appreciate a few extra niceties. What I wrote in March, 2023:
https://protesilaos.com/codelog/2023-03-25-emacs-restart-mct-development/.
What does MCT offer that the built-in Emacs UI does not? In short:
MCT provides a facility for “live completions”, to automatically
update the *Completions* buffer given certain conditions. A
number of user options control the specifics.
There are user options for a passlist and blocklist, which determine
what should automatically display the *Completions* buffer and be
live updated. The passlist and the blocklist can target individual
commands, such as find-file, as well as completion categories like
buffer. The manual includes a section with several known
completion categories.
To be clear: MCT builds on top of the built-in functionality and
should not compete with it. Depending on my availability, I will try
to prepare patches for emacs.git to see whether at least some features
can be added directly to mnibuffer.el or related.
MCT supports Emacs 29 or higher
MCT is highly opinionated about how the completions should work. This
applies to the presentation of the completion candidates as well as
the behaviour of commands that cycle between the minibuffer and the
*Completions*, treating the two as a contiguous space. In previous
versions of Emacs, MCT could not work exactly as intended due to
limitations in the underlying framework. For example, the variable
completions-format gained the one-column value only in Emacs 28:
Emacs 27 supported grid views which are not intuitive as a vertical
list for up-down cycling between the candidates.
To make things easier to maintain, MCT only works with Emacs 29 or
higher. The ~1 year hiatus has hopefully given users enough time to
assess their options.
Deprecation of mct-region-mode
For a while, MCT supported in-buffer completion via a minor mode that
would add all the needed functionality. This was always problematic
due to underlying constrains and is thus no longer supported. MCT is
designed to work exclusively with the minibuffer, where the behaviour
is more reliable.
Nevertheless, users can still get an MCT-like experience with these
settings, which affect the default UI (modify as you see fit):
;; Define the small wrapper functions(defunmy-mct-next-line-or-completion(n)"Select next completion or move to next line N times.
Select the next completion if `completion-in-region-mode' is
active and the Completions window is on display."(interactive"p")(if(andcompletion-in-region-mode(mct--get-completion-window))(minibuffer-next-completionn)(next-linen)))(defunmy-mct-previous-line-or-completion(n)"Select previous completion or move to previous line N times.
Select the previous completion if `completion-in-region-mode' is
active and the Completions window is on display."(interactive"p")(if(andcompletion-in-region-mode(mct--get-completion-window))(minibuffer-previous-completionn)(previous-linen)))(defunmy-mct-return-or-choose-completion(n)"Choose current completion or create N newlines.
Choose the current completion if `completion-in-region-mode' is
active and the Completions window is on display."(interactive"p")(if(andcompletion-in-region-mode(mct--get-completion-window))(minibuffer-choose-completion)(newlinen:interactive)));; Get the key bindings(let((mapcompletion-in-region-mode-map))(define-keymap(kbd"C-n")#'my-mct-next-line-or-completion)(define-keymap(kbd"C-p")#'my-mct-previous-line-or-completion)(define-keymap(kbd"RET")#'my-mct-return-or-choose-completion));; Tweak the appearance(setqcompletions-format'one-column)(setqcompletion-show-helpnil)(setqcompletion-auto-helpt);; Optionally, tweak the appearance further(setqcompletions-detailedt)(setqcompletion-show-inline-helpnil)(setqcompletions-max-height6)(setqcompletions-highlight-face'completions-highlight)
The mct-minibuffer-mode is renamed to mct-mode
The mct-mode was the original name, which was later given the
“minibuffer” specifier to disambiguate it from the aforementioned
mct-region-mode. With the latter gone, this qualification is no
longer pertinent and the original name can be restored.
The completing-read-multiple indicator has been removed
Previous versions of MCT would prepend a [CRM] tag to the minibuffer
prompt of commands powered by completing-read-multiple. While this
is a nice usability enhancement, it is not specific to MCT and thus
should not be part of mct.el. Use this in your init file instead:
;; 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.(defuncrm-indicator(args)(cons(format"[`crm-separator': %s] %s"(propertize(replace-regexp-in-string"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'"""crm-separator)'face'error)(carargs))(cdrargs)))(advice-add#'completing-read-multiple:filter-args#'crm-indicator)
No more IDO-like file navigation
Older versions of MCT had a command for file navigation that would
delete the whole directory component before point, effectively going
back up one directory. While the functionality can be useful, it is not
integral to the MCT experience and thus should not belong in mct.el.
Add this to your own configuration file instead:
;; Adaptation of `icomplete-fido-backward-updir'.(defunmy-backward-updir()"Delete char before point or go up a directory."(interactivenilmct-mode)(cond((and(eq(char-before)?/)(eq(mct--completion-category)'file))(when(string-equal(minibuffer-contents)"~/")(delete-minibuffer-contents)(insert(expand-file-name"~/"))(goto-char(line-end-position)))(save-excursion(goto-char(1-(point)))(when(search-backward"/"(minibuffer-prompt-end)t)(delete-region(1+(point))(point-max)))))(t(call-interactively'backward-delete-char))))(define-keyminibuffer-local-filename-completion-map(kbd"DEL")#'my-backward-updir)
Lots of changes under the hood
I do not intend to refashion MCT. It works the way it was originally
intended to. What I did is to streamline the code for compatibility
with Emacs 29 and tweak the custom commands to preserve the desired
cyclic behaviour between the minibuffer and the *Completions*.
Experiments such as integration with the avy package or the ability
to type-to-complete in the *Completions* buffer are abandoned.
Do not expect radical changes henceforth. I shall monitor and/or
contribute to developments in core Emacs and am happy to forever
archive MCT if/when the default completion UI gains the capabilities
that, I think, make the user experience a little bit easier.
I like generating long LaTeX documents from org-mode because, for one thing, org-mode has nice section folding. But not everyone I work with uses Emacs, so its better to work in LaTeX directly rather than have Emacs generate LaTeX.
AUCTeX has section folding for LaTeX documents, though so far I’ve only has limited success at getting it to work. However, RefTeX worked right out of the box.
If you enter reftex-modectrl–c= then RefTeX will open a table of contents window.
Scrolling through the table of contents window scrolls through the body of the document. This isn’t exactly section folding, but it serves a similar purpose.
RefTeX ships with Emacs, so there’s probably no need to install it, but the mode is not enabled by default.
If you’re an even moderately experienced Emacs user, you probably know about batch mode. You can write a file with some Emacs commands and run it either by calling Emacs on the command line with a --script parameter pointing to the file or by adding at #! line to the top of the script and running it by calling the script directly.
Emacs Elements has a short video on batch mode that describes a use case I hadn’t seen before. The video is only 5 minutes, 45 seconds so you can watch it to get the details but the TL;DR is that you can use batch mode to check a changed init.el file for errors.
The idea is that you use batch mode to load the modified init.el file. If there are any errors, you get notified but your active Emacs instance is still operable. It’s preferable to just restarting Emacs because if there’s a disabling error, you still have a working Emacs to correct it. Sure, you could always restart Emacs with a -q or -Q option but then you don’t really have a fully operational Emacs to work with. That happens to me a lot when I have an error in my init.el. I can get by, of course, but it’s a pain. With Emacs Elements’ method you still have your familiar and comfortable working environment available.
I’m hungry, and my wife is hungry, but I want to write this page, and my wife is reading about mutations and genomes and what not, so we keep postponing it. It’s now 21:36.
If you’re interested in #Gemini, a (small) hypertext world outside the world wide web, there are a few options out there. The Transjovian Capsules is a project I host that works well with Emacs (you know me) and Lagrange (a Gemini client by @jk).
Here’s how you would get started:
Transjovian Capsules (edit: no longer active 😢)
These instructions available on the web, of course, since there needs to be a way to cross over…
I’m still writing the documentation, so if you get stuck, mail me, and we’ll fix it, together. 🥳🚀🚀
Today, we have a bit of humor that we can all relate to. Reddit has a post announcing the Web browser NYXT inspired by Emacs. I don’t know anything about NXYT but I did love this comment from noooit:
Now I have a weight loss org table defined and therefore an easy way to track the total amount of weight loss each week I had an idea for an improvement with some form of a satisfying graphical representation.
I have been aware of gnuplot for while now and I think this is an opportunity to give it a try through org-mode
The first thing to point out is that as I was learning more about org tables I came across the orgtbl-ascii-draw function which visually is quite self explanatory as shown in the bar column. The main arguments are the x axis range (150-220) and the span in characters (15)
Now on to the main plotting change and that is defining a #+PLOT header setting out all the parameters I want fed into gnuplot from the org table.
Again most of the defines are self explanatory and the only issue I ran into was trying to get plot lines to be rendered based off the original first date column. The histograms display did work and correctly listed each week with the corresponding bar value but lines only displayed a single vertical line at a 2023 value.
I am guessing in the case of a line display the data is plotted in a more non discrete manner and the date was parsed as best it could and just plucked out the first part of the date string which was the year. I decided not to investigate this and instead simply added another column with a simple numbering scheme and set my x axis off of that. I only want to see a plot over a number of weeks so I don’t care about the display of the date.
So now lets generate the gnuplot! which can be accomplished by running C-c " g (org-plot/gnuplot):
The altcaps package is a small, focused-in-scope tool that helps
users communicate mockery or sarcasm effectively. It does this by
alternating the letter casing of characters in the words it affects.
Backronyms: Alternating Letters Transform Casual Asides to Playful
Statements. ALTCAPS Lets Trolls Convert Aphorisms to Proper
Shitposts.
Below are the release notes.
Breaking change to the value of altcaps-force-character-casing
This user option enforces the specified letter casing for the given
character. The value is an alist. In previous versions, the car of
each cell was a character type, whereas now it is a string type.
Concretely, the old value was expressed like this:
;; Old value(setqaltcaps-force-character-casing'((?i.downcase)(?l.upcase)))
It becomes:
;; New value(setqaltcaps-force-character-casing'(("i".downcase)("l".upcase)))
At least based on my correspondence, strings are easier for users.
The notation for characters causes confusion.
The public altcaps-transform function
This is the function that performs the alternating letter casing, while
also respecting the user option altcaps-force-character-casing. The
function is more efficient now. Use it in Lisp with a single string
argument, like this:
(altcaps-transform"Your wish is my command");; => yOuR wIsH iS mY cOmMaNd
The above return value is consistent with the default settings. With
altcaps-force-character-casing bound, we can affect the output thus:
(setqaltcaps-force-character-casing'(("i".downcase)("m".upcase)))(altcaps-transform"Your wish is my command");; => yOuR wiSh iS My CoMMaNd
Characters without casing no longer matter
Before, the algorithm was toggling the letter casing of virtually
every character. This means that a string like "a.c" was wrongly
treated as a sequence of three characters with letter casing, so the
program was trying to do this:
a => downcase
. => upcase
c => downcase
Whereas now, the transformation skips characters without letter
casing:
a => downcase
. => i Am ThE iNtElLiGeNtSiA nOw
c => upcase
The altcaps-replace is superseded by altcaps-replace-region
The altcaps-replace was not sufficiently abstract, making the code a
bit repetitive. The new altcaps-replace-region is efficient in that
regard.
The arity of the two functions is different: altcaps-replace was
accepting one required argument plus an optional one, while
altcaps-replace-region takes three arguments at all times. Please
consult its doc string before adapting it to your code.
Auf der Hexkarte hat es natürlich auch unterirdische Höhlen und
Kammern, Hallen und Höhlen – aber ich halte diese extra einfacher als
einen Megadungeon. Wenn ich das nicht mache, kann es sein, dass es
niemanden interessiert, oder es interessiert so brennend, dass sich
niemand mehr für die Wildnis interessiert.
Es geht also darum, “kleine” Dungeons zu machen. Grundsätzlich hat es
hier nicht sehr viel Info. Zwei drei Monstersachen, Schätze, ein paar
Sätze, mehr nicht. Für den oberen Teil hatte ich folgende
Beschreibung:
0608: On one of the rock faces you can still see the markings of
the old dwarf forge Great Hammer. The ruin is abandoned and dead. If
you explore the ruins, you will soon find the balorThe Wounded
Sceptre of the Gods who caused its downfall (HD 9+1 AC 4 1d6/2d6 F9
MV 12 ML 11; flying; aura of fire (anybody in melee takes automatic
1d6 fire damage); if the first attack with the flaming whip hits, the
second attack with the flaming sword is at +4; only harmed by magic or
magic weapons; immune to fire).
The orcs of the Filth-Spitting tribe serve The Wounded Sceptre of
the Gods. 30 orcs led by Maufimbul (HD 7) live here (HD 1 AC 6
1d6 F1 MV 12 ML 8 XP 100). […]. The ancient spear Fur Hunter +1/+3
vs. mammals, a relic of the Old Lizard Wars, blessed by Set.
Maufimbul is a famous engineer in the Ash and Steel school of
Machinery.
Also here is cautious Grishg (HD 3). […] He likes to keep a low
profile. He joined Maufimbul when things got hot up in the north.
Shagdog, last of the Fingerslayers has joined Grishg.
The orcs have built their settlement in the main hall, along the walls
and overlooking the stone garden and its dwarf statues and the
artificial pools. The round huts are protected by 2 boars (HD 3+1
AC 6 1d8 F1 MV 15 ML 9 XP 300). Every room has multiple little painted
wooden statues depicting animals and plants from the surface world:
goats, yaks, trees, mushrooms.
Gestern wusste ich: Am Abend habe ich drei Spieler mit jede Menge
Unterstützung, die den Orks ihre Schätze abnehmen wollen. Die Elfe
Jada hat zwei Kampfmagier der 5. Stufe auf Riesenskorpionen, die je
einen Feuerball und zwei magische Geschosse mit je drei Pfeilen
verschiessen können, plus eine weitere Elfe und einen weiteren Dieb.
Der Mörder Hammond Vex hat einen Elfenumhang und Elfenstiefel, kann
also fast perfekt schleichen, hat einen bezauberten Waldschratt und
drei bezauberte Riesenaffen im Gefolge. Der Zwerg Unn hat eine Diebin
im Gefolge. Am Schluss waren es 13 Leute + 2 Riesenskorpione der
Kampfmagier + Unns Reitschwein.
Bezüglich der Anzahl Gefolgsleute halte ich es so: keine Rekursion.
Die Gefolgsleute haben also nicht selber wiederum Gefolgsleute. Jeder
kann ein Pferd oder sonst ein Reittier haben, aber wenn das Reittier
besondere Befehle befolgen oder kämpfen kann, dann zählt es zum
Gefolge. Das Charisma der Spielfiguren bestimmt, wie gross das Gefolge
jeweils sein darf. Im Fall von
Jada ist es
so, dass das Gefolge auf 5 Personen, Reittiere und sonstige Begleiter
beschränkt ist: die Kampfmagier Gwena und Sida, die Diebin Lilly, die
Elfe Laetitia, und der Nekromant Sherwin. Dieser beschwört mit einem
Spruch später noch einen Ghul, aber dieser gilt als Sprucheffekt,
nicht als Gefolgsperson. Somit ist klar, auf den Riesenskorpionen kann
man reiten aber sie kämpfen nicht. Falls sie kämpfen würden, dann wie
freie Monster: sie greifen an, wen sie wollen. Das heisst, die
Spielleitung bestimmt ihre Ziele durch würfeln.
Dann habe ich mir die Werte der Monster rauskopiert. Wie stark sind
sie, wie viele gibt es, was würden sie zaubern, und so weiter. Hier
ist mir persönlich wichtig, dass es mehr als nur 1–2 Monsterarten gibt
und sie nicht zu kompliziert zu führen sind. Deswegen ist mein Balor
Dämon auch einfacher als ein regulärer Dämon aus den offiziellen
Regelwerken. Die sind mir viel zu kompliziert mit all ihren
Fähigkeiten. Da gebe ich ihnen lieber noch ein paar Höllenhunde mit
und gut ist.
Dann habe ich mir eine Liste von Orten aufgeschrieben. Dabei lasse ich
mich von der ursprünglichen Beschreibung inspirieren. Was haben wir
denn dort? Eine Haupthalle, einen Steingarten, Statuen von Zwergen,
irgendwelche Wasserbecken.
In einer gewissen Weise kann man sich das als “point crawl”
vorstellen: Ich bennen die Knoten und die Verbindungen sind eher
allgemein gehalten. Das sind einfach breite oder schmale Tunnels, enge
oder steile Treppen, Schächte, und so weiter.
Mit dieser Information male ich mir eine Skizze der Karte. Die ist mit
der Füllfeder gekritzelt, ohne ästhetische Ansprüche. Ich nehme die
Liste der Orte als Inspiration, vergesse ein paar davon (die
Wasserbecken und die Statuen), zeichne noch ein paar zusätzliche Orte
ein (die Schornsteine, die Schlackenhalde).
Auf der Karte notiere ich mir gewisse taktische Situationen. Wie sieht
das Tor aus? Wie wird die lange Treppe verteidigt? Auf die
Schützengalerie bin ich besonders stolz.
Also:
einen Überblick über die Spielfiguren und ihr Gefolge
die Monster und ihre Werte, vielleicht eine Tabelle für
Zufallsbegegnungen
eine Liste der wichtigen Orte
eine Skizze der Umgebung
Jetzt kann das Abenteuer beginnen! Im
Spielbericht
sieht das dann so aus:
Hammond Vex, Pfote, Tick, Trick, Track, Jada, Gwena, Sida, Lilly,
Laetitia, Sherwin, Unn und Nela ziehen in den Riesigen Riesen gegen
die Orks von Grosshammer… Die beiden Kampfmagier reiten auf ihren
Riesenskorpionen, Unn reitet auf seinem Eber…
Am Fuss der Berge trifft Hammond Vex auf zwei Waldschratte, die sich
als Spione zur Verfügung stellen. Ihr erster Auftrag: Verhindern,
dass Ork Patrouillen der Gruppe nach Grosshammer nachziehen. Haltet
den Weg zurück frei!
Bald windet sich der Weg in die Höhe, auf den kleinen Vorplatz von
Grosshammer. Hinter den Toren hat es vier Orks, die relativ kurz
angebunden sind und schnell schiessen. Doch die Magier schiessen mit
magischen Geschossen zurück und der letzte wird bezaubert und öffnet
das Tor. Dieser Ork wird geschickt, die Wachen auf der
Schützengalerie zu holen, doch scheint die Sache auf zu fliegen: ein
Horn ertönt. Die Gruppe macht sich bereit, will die anstürmenden
Orks in dem etwa 20m langen Gang vom Torhaus mit Zauberei töten.
Bald hört man die Orks singen, Trommeln schlagen, pfeifen, und man
hört den Balrog, der seine Höllenhunde in den Gang treibt. Ein
Kampfmagier schiesst einen Feuerball in den Gang – doch die Hunde
sind immun! Ein Gemetzel beginnt im Torhaus. Als der letzte
Höllenhund erschlagen ist, stürmt, der Balrog durch den Gang, fängt
Feuer, die Peitsche brennt, das Schwert brennt, alle im Nahkampf mit
den Höllenhunden werden von Flammen umhüllt, das Stechen geht weiter
– und dann geschieht das unglaubliche: Hammond Vex ersticht den
Balrog, den Herrscher der alten Zwergenbinge Grosshammer.
Mit Schwert und Peitsche des Balrogs macht die Gruppe sich auf den
Rückweg. Sie sind geschwächt. Und wer weiss, was die 70 Orks noch
machen.
Ich bin zufrieden!
Die Beschreibung der Hexkarte wird angepasst, der Balor entfernt, die
Anzahl Orks verringert, dann um 1W8 Orks aus der Umgebung wieder
erhöht, und jetzt sollte ich wohl überlegen, wie es das nächste Mal
weiter geht. Haben die Orks eine Chance, jemanden in der Nähe um Hilfe
zu bitten?
It’s often said of Emacs that it includes everything including the kitchen sink. Indeed, there’s even an Emacs logo reflecting this sensibility. I don’t know if it was ever an official logo but its use is widespread. Still, it would be easy to dismiss the notion as a bit of Emacs fanboy hyperbole.
Gopar has a short video that provides compelling evidence that Emacs does, indeed, include the kitchen sink and everything else. The video demonstrates two builtin functions. To say that they are obscure and little used would be an award winning understatement.
The first is a function that translates to and from Morse code. Just in case you want to know what “Emacs is amazing” looks in More code, Emacs has you covered:
./--/.-/-.-./... ../... .-/--/.-/--../../-./--.
The second functionality is to render text in the phonetic alphabet. I’m familiar with this from my pilot days, where it can increase clarity in radio communications, but didn’t expect to find it in Emacs core. If you want to know how to spell Emacs in the phonetic alphabet, here you go:
Echo-Mike-Alfa-Charlie-Sierra
Emacs calls this the “NATO alphabet”. I’ve never heard it called that before but whatever you call it, Emacs can render text into it and, of course, go in the other direction as well.
Again, these functions are builtin, not packages. They make the current agonizing over whether or not to add actually useful functionality to Emacs core look positively silly.
Intro I had a series of todo entries like this
*** TODO Rename legacy/authtoken.clj and sibling tests https://github.com/FOO/BAR/issues/158 and wanted them to have the issue number at the end of the todo title, which helps with our git branch naming praxis. Emacs regular expression (slightly improved) anzu-query-replace-regexp1 with the following:
^\(\*.*\) \(.*github.*\)\([0-9]+\)\s-+ → \1 #\3 \2\3 Where the linebreaks are entered literally via C-q C-j.
The end result was what I wanted:
Let’s go back to the official plugins page.
This guy has made a bunch of plugins.
Who is he?How is he able to make such good use of Kakoune?Oh, he’s an Emacs user!Of course!
Yeah, I am.
Even though the video is about Kakoune, the author’s main focus is note-taking and the oddities that come with this process when using a code editor to edit text.
Kakoune advertises itself as a code editor for the most part, and I have to agree.
As far as I know, Maxime Coste (@mawww), the creator of Kakoune, made it because of the desire for a better programming experience.
As a result, a small and contained code editor was made.
And I can appreciate a desire of the system one can fully grasp and understand.
Kakoune is relatively small, compared to Emacs and Vim that is.
It doesn’t feature a scripting language, instead relying on shelling out if you need any programmable features.
It’s a clever trick, and the editor exposes its internal state as a set of shell variables, so you still can do interactive things based on your workflow.
And I did a lot of this back in the day when I used Kakoune.
I have mentioned Kakoune in this blog previously, but it was rather sparse.
The reason for that is stated in the video pretty accurately - I use Emacs and not Kakoune, so there’s little to no reason for me to write about it besides occasional praise or comparisons.
I mention it on my about page, and in various text-editor-related posts, but that’s it.
I don’t participate in the Kakoune community anymore, and no longer actively maintain my packages, as I no longer use Kakoune.
But I still need to address this point of the video - I wasn’t an Emacs user when I started with Kakoune.
Before Kakoune I was a Vim user!
And transition from Vim to Kakoune was caused by several factors, one of which is again stated in the video:
…and people have written so many damn Vim plugins over the years that if you have a need it’s already been addressed like three or four different ways.
So with Vim you could just piece together your ideal text editor like LEGO bricks…
And that’s exactly my problem with Vim - too many ways to do the same thing.
At the time, I was working with SoC in C and started using the Ale plugin for asynchronous linting of the project, as the synchronous linting was quite slow.
This was before LSP inception - just look at how many tools Ale supports.
As far as I remember LSP was added to Ale much later, when competing plugins showed up.
Competing plugins.
There’s nothing bad with competition on its own, and in Emacs, this is also present, with many plugins, but in Vim’s case, I feel that people created most of the plugins purely because of the NIH1 syndrome.
I lost count of how many plugins provided autocomplete interfaces, there were tons of list-narrowing frameworks, plugin managers, and snippet managers, and everything was poorly integrated with each other.
I remember that some autocomplete plugins did not integrate well or at all with snippets, some synchronous completion providers were not supported by asynchronous completion frameworks, and so on.
Again, this was before LSP came to the scene and basically became a standard for these features.
So perhaps the situation is a bit better today, but I still have doubts.
NeoVim people are going crazy over Lua API, writing their configs in Fennel, and making new Lua-based plugins that may or may not be compatible with the rest of the ecosystem.
Fact is, you can piece your dream text editor like LEGO bricks, just beware that some of these bricks are actually Duplo blocks, some are COBI bricks, lots and lots are probably LELE, and some are even freakin’ OLEG.
Well, at least that was the situation when I used Vim, I gave up on updating my config around 2018 and made the switch to Kakoune.
By that time I already made three plugins for Vim because I was unsatisfied with existing ones, but they were crappy too.
Integrating these plugins into different other plugins was a huge pain.
So I made the switch.
You can trace the history from that point if you’re interested in my Kakoune journey (why would you be though).
Obviously, I missed a lot of features from Vim, and Kakoune actually has an entry on their wiki on how to migrate from Vim.
Unfortunately, though, the suggestions were either too hardcore-minimalist or uncooperative.
For example, Vim’s smarttab feature didn’t exist, and expandtab was suggested to be done via hooks.
Not that it was wrong, but it was suggested when people asked about a very specific feature of Vim, and these did not provide the same feature as in Vim.
So I started writing plugins.
However, Kakoune didn’t have conventional plugins at all at that time.
Well, there was a section with plugins on the official page, but there was no real ecosystem.
There was nothing such as Vimplug if you will.
Installing plugins meant you had to manually copy files around, or load them pathogen-style, but the process wasn’t convenient or easy to automate in my opinion.
Updating plugins installed in this way was problematic too.
This motivated me to make plug.kak.
And then I started experimenting more and more with other interesting plugins.
But, around the same time I switched to Kakoune, I briefly tried Emacs.
In reality, I tried Emacs like 4 times at that point, the earliest one dates back to around 2010.
All four times I did not succeed, but something gravitated me to it for some reason.
This last one actually was a reason why I made some plugins like langmap.kak or kaktree, which resemble what I saw in Emacs at that point.
Many plugins were inspired by Vim, like smarttab.kak, powerline.kak, fzf.kak, tagbar.kak, equivalents to which I daily used in Vim before.
And at that time, Kakoune really did everything I needed and was a very capable code editor.
But I still wanted something more.
So why I made the switch to Emacs - but for a bit different reasons.
First of all, I started enjoying writing more prose instead of just writing code.
And if you’ve watched the video I linked above, the author similarly wants a text editor, not a code editor.
There’s another video on their channel about note-taking, featuring a lot of programs made specifically for this task, and it features a text editor section at the end in which the author talks about Emacs and Vim, briefly touching Kakoune and Helix.
What they’re saying about Emacs is also very similar to what I’ve experienced, although I didn’t use an Emacs distribution, I started with vanilla2.
Org Mode.
I don’t know how to explain this to a non-Emacs, non-Org person, but every time someone asks the “Why Emacs?” question, Org Mode is somewhere at the top of the answers.
And I never understood that, until I tried for myself.
And boy are they right.
But before I understood that, there was a long period of adoption.
I still used Kakoune, but more and more I was shifted towards Emacs - it slowly consumed me.
A big part of that was that I started writing in Lisps.
Emacs is the king when it comes to Lisp editing.
Plugins for various Schemes, that I used to do tasks from the SICP book were amazing, and Kakoune :repl command paled in comparison.
Though I can’t blame anyone here - Emacs is a lisp machine on its own, it’s bound to have great lisp editing experience.
Though lisp wasn’t my primary language back then, more like a novelty.
I used Rust and considered switching jobs from a C engineer to a Rust back-end developer.
Rust seemed both a perspective and a safe enough bet for the foreseeable future.
Who knew how the tables would turn?!
Kakoune actually was great as a Rust IDE of sorts.
The kak-lsp plugin was on it, written in Rust it supported Rust well.
And it helped me at work with C too.
That was 2019, the year I started using Emacs for real.
That year, I made my first, kinda big post, in which I realized that I wanted to write more.
Ironically, it was a post about Emacs on the Kakoune forum.
It was even written partly in Emacs and partly in Kakoune - I was comparing editors at that time, much like the author of already mentioned videos.
But this day signified that I was ready to fully migrate to Emacs - my config was more or less ready for work at that point.
Emacs seemed better at writing, although I was missing cool Kakoune features, such as multiple selections, a lot.
I started writing this blog in 2020, and it was done in Emacs from the get-go.
Not so long after that, I moved to Emacs completely.
I used Kakoune for 1.9835616438356165 years (first commit on Jul 24 2018, last commit on Jul 17 2020).
But this post actually is called “Why Kakoune” and not “Why I switched to Emacs”, so let’s address that!
Why Kakoune
What an awfully long preamble.
If you read that, you have my thanks.
If not - fair enough.
I think it’s kinda weird to read reasoning on why someone should use Kakoune from someone who’s not using Kakoune right now and hasn’t for another three years already.
But, as far as I can see, not much has changed in Kakoune since!
Which, actually, is great - I can actually just check out to a commit previous to the one I deleted my Kakoune config in the dotfiles repository, and run it.
A fresh clone of Kakoune’s latest stable release builds in just two minutes on my machine, and loads my old configuration without too many errors:
At this point of the post I wanted to write about stability, but as it seems, the situation isn’t that great.
Some plugins simply no longer exist.
Some defaults were changed in 2022, making keys behave differently (can be turned back via a remap)
Some changes were made to how Kakscript is interpreted.
Most of my plugins broke (but that’s on me).
There’s possibly more, but I’m out of the loop.
What’s hasn’t changed is that peoplestillstumble on the autoload directory after all these years.
Because there’s no plugin manager in Kakoune, it relies on storing scripts you want to load automatically during startup in the ~/.config/kak/autoload directory.
This, however, for some weird reason, disables loading a system-wide Kakoune autoload directory, and Kakoune simply stops loading all of its inbuilt features that are shipped as .kak files.
I also experienced this problem, and it was one of the main reasons for making plug.kak.
So, if anything above seems too weird, perhaps Kakoune is not for you.
But, given all that, Kakoune hasn’t changed that drastically over the three years I haven’t used it, and that’s a good thing.
Even now, I can still edit files in it pretty comfortably after my brain does the switch from Emacs keybindings to a modal model.
For the most part, that is, some habits are hard.
But one thing, that I think can be a main reason why people should try Kakoune, in my opinion, is its POSIX integration.
Back in the day, I really liked this idea, can’t say so today3, but, it’s still a good reason why Kakoune is interesting.
I mentioned that I made plugins for Kakoune, and because of that I now know POSIX sh pretty well.
Not that I need this knowledge that often, but when I do, I’m glad Kakoune taught me well.
The same goes for other POSIX tools - Kakoune basically forces you to learn your shell stuff, because there’s no other way to be productive in Kakoune.
Everything is done via shelling out to use some tool like fmt, grep, find, etc.
And when shell tools are not enough you can always call different programming languages from the shell.
For example, some of my plugins are written in Perl of all languages.
And while I can’t say that I’m proud of that, or that I know Perl that well, I can still say that Kakoune made me learnPerl, well, to some degree.
Also, Awk.
And I still occasionally use both when I need to send a code snippet to my colleague so that they can send me some filtered logs instead of full logs.
Because that’s what these tools excel at, and learning how to use them from within an editor really makes it apparent how they can be useful.
So Kakoune really helps you learn your standard tools, and some extra things too.
Another thing I think can be said is that Kakoune really makes you learn and understand regular expressions.
When I started using Kakoune, I once told my friend that I started using an editor that is built around using regular expressions for text manipulation.
They were quite skeptical, because I didn’t know regular expressions back then, and they had some experience and said that it’s a terrible idea to use them at all.
But turns out, that regular expressions are actually easy to learn, and Kakoune really helps with that, because you’re constantly creating multiple selections, selections in selections, and filtering selections - all done with regexes.
So, if you think that regexes are hard and you’ll never learn them (and you’re a Vim user by chance), give Kakoune a try.
And finally, Kakoune is just fun!
Especially if you’re a seasoned Vim user, the inverted paradigm of object-verb really messes with your brain.
I think Kakoune features a really unique editing model, where it doesn’t need any separate mode for selecting text - all motions do it automatically.
When I started, I adjusted to the object verb paradigm pretty quickly, it’s very natural to how things are done - in real life, we usually don’t think upfront what we want to clean and then how many of what was that, ah yeah the shelves.
We think that these shelves are dusty and we need to clean them.
I should probably do it right now.
Anyway, a TL;DR for this could as well have been:
Kakoune gives you:
Small and understandable core.
Proficiency with POSIX tools,
and maybe even some programming languages other than sh.
Structural regular expressions as a central way of text manipulation.
With multiple selections created via regular expressions, acting upon regular expressions.
Fresh take on the modal editing paradigm.
So, yeah, Kakoune definitively deserves your attention, if you’re into experiments with your workflow.
I, certainly, am.
At least, I was, now I do everything from Emacs.
Mike Zamansky has popped in with another Learning Elisp video before he departs on his vacation. He didn’t want to start the next project and leave it hanging while he was away so this video covers some preliminaries. In particular, he covers some Elisp builtin data structures. That includes lists, pairs, vectors, hash tables, and association lists.
Vectors and hash tables have direct analogs in other languages and are probably familiar to all Irreal readers. Lists are simply linked lists and although they don’t have a builtin analog in most other languages, the idea is a common and familiar.
Zamansky explains how lists and pairs (single, unlinked cons cells) are implemented and then goes on to discuss the data structure that he’s planning to use in the next project: association lists. They’re sort of an intermediate structure, a bit like hash tables but lighter weight and not as fast. They’re the ideal key/value lookup method for small tables. It’s \(O(n)\) instead of the \(O(1)\) that hash tables provide but it’s perfect for relatively small lookup tables.
The next project is going to be about inserting emoji into a buffer and Zamansky is planning on using an association list to map the emoji name to the actual symbol. We’ll have to wait until Zamansky gets back from Europe for that but we can consider this video a teaser.
The video is 16 minutes, 16 seconds long so plan accordingly.
Thinking about web mentions because of a post by @hbuchel I just read. I remember having added them to my wiki so that it would basically just post a comment. Then I started seeing spam, so I started checking whether the source actually linked to the page it claimed to link to. Then I realized that practically nobody was doing it. I had one person posting articles on their block and using web mention to ping my site. I guess they used a Wordpress plugin. Sadly, that plugin pinged my site no matter how insignificant the link. A few times the web mention didn’t really add value and eventually I disabled it. 😿
What seemed more useful, eventually, was using referrals. Browsers still send them along even though it’s a privacy issue, and I used reused the ideas I had already developed: see if the URL is readable, does it actually contain a link to my site, plus a blocklist that removes search engines, some pattern matching to try and get canonical URLs (with and without www subdomain, with and without https scheme, with and without certain path info and the like – a lot of Blogspot specific trickery), and on and on… so it was still a lot of work. Ugh!
But the signal was more interesting, I felt: a referral usually meant an actual person followed a link because it seemed interesting. I discovered Reddit threads and blogs linking to my pages. There was joy on a regular basis.
The only stain on the endeavour was that I still feel like this is a privacy violation. The signal I am using shouldn’t even be there. Plus the canonicalisation of URLs was annoying.
I don’t think I will be adding either of the two to this site.
I find myself working on forward indexes and feeds for Oddµ. Or
perhaps I should say: outside of Oddµ. I’m still holding off because
it seems to be a very particular way of thinking about a wiki. My
problem is that I use the wiki like a blog or Zettelkasten and
therefore a feed is appropriate for my wiki.
So the first question is: Was I wrong when I wondered about feeds?
A feed is a dubious addition to a digital garden or a Zettelkasten.
Yes, the ideas can be put in a chronological order, but “following
along” is perhaps not the most efficient way of following along. –
To have a feed or not
If the wiki is a site where visitors come to see what’s up, then the
site is a publishing platform more in line with news. This points at
the wiki duality: a digital garden where people work on pages like
they would on papers to be published later or not at all, linked to
from elsewhere; and a blog with updates of thoughts and projects. I
find myself using the wiki like a blog with updates, not like a
digital garden.
In order to use the wiki like a blog, the only thing that really seems
to be missing is forward indexes. A forward index in this context is
simply a list of links. On the front page, a list of links to “new”
pages. On a topic page, a list of links to “new” pages for that topic.
For Oddmuse, I used “journal pages”. These pages transcluded the last
ten appropriate pages, resulting in that wall of text
vibe that I liked. This is also what many of the older blogs look
like. If you wanted to find the older pages, you had to click through
the pages, or you had to navigate an archive with dates. Neither of
which was something I particularly enjoy.
What I did do a lot on my old wiki, however, was search for page
titles and tags. Now, hopefully the search Oddµ has is good enough to
offer that – we’ll see – but topic pages like RPG having a long
list of links for easy browsing by page name seems like a good idea to
me.
All of this to say that right now I have a Perl
script that I call on the command-line, for
now. I might call it as a CGI script one day, who knows.
For every file I give it as an argument, it does the following:
For every hash tag the file contains, it does the following:
add an item to appropriate feed, if it exists (not all hashtags need
their own feed, I think)
add a link to the topic page, if it exists
To populate new topic pages with links to existing pages, I have used
the following pipeline from hell: grep --ignore-case
--files-with-matches '#' wiki/2023*.md|sort --reverse|perl -e 'use
Encode; use Mojo::Util qw(url_escape); use File::Slurper
qw(read_text); binmode(STDOUT,":utf8"); while(<>) { chomp;
$d=read_text($_); ($t)=$d=~/^# (.*)/m; s/\.md//; s/^wiki\///; $s=$_;
$s=~s/_/ /g; $n=url_escape($_); print "* [$t]($n)\n"}' … what can I
say? I think it works. 🤪
As many of you know, I’m very interested in the use of Emacs for non-technical purposes. In particular, I interested in how prose writers and researchers in disciplines other than Computer Science and its close siblings use Emacs for their writing.
Peter Prevos has written a lot about using Emacs for writing and he’s gathered these posts together into an Emacs Writing Studio section on his blog. As Prevos puts it, “The Emacs Writing Studio configuration shows how to use Emacs to conduct research, write your prose and publish your text as an article, (e)book, or website.”
It’s an excellent resource conveniently broken into small easy-to-read articles. There are 28 articles—although some of them are only peripherally about writing. The other aspect of the Emacs Writing Studio is that it includes a downloadable configuration to get the n00b started using Emacs for writing.
In his first article, Prevos notes that the right way to approach Emacs is to learn just enough to get started and then learn other details as they become necessary. As even moderately experienced Emacs users know, it’s a fool’s errand to try to learn everything about Emacs up front. Prevos suggests the same incremental approach that Irreal and others have recommended for Org mode.
If your principal activity is writing prose, you should take a look at Prevos’ site. It is, as I say, a nice resource.
50% GDP destruction somewhere between 2070 and 2090
I’m still trying to process this. The situation might be so much worse
– and I always thought I was a pessimist. This is from page 26 in
“The Emperor’s New Climate
Scenarios:
Limitations and assumptions of commonly used climate-change scenarios
in financial services” by Sandy Trust, Sanjay Joshi, Tim Lenton, Jack
Oliver.
There is uncertainty around how much warming we will experience. As
described in the previous section, atmospheric GHGs are now double
their pre-industrial level, which is what ECS is calibrated to. A
reminder that best estimate ECS = 3°C but there is an 18% chance
that ECS>4.5°C.
Earth-system sensitivity is greater than ECS, as ECS assumes ice
sheets and vegetation fixed, with a possibility that ESS is
significantly greater than ECS. Conservatively, there is an argument
for at least a 20% chance that we may be on a trajectory to 5°C or
more of warming at current levels of GHGs.
The pace of warming is also uncertain. However, some scientists now
estimate warming of 0.3 °C per decade or around 1°C every 30 years,
which would imply warming of greater than 2°C by 2050 and 3°C by
2080. This is well within life expectancy for many in workplace
schemes now and in range for the European Insurance and Occupational
Pensions Authority (EIOPA) who have specified 80 years as long range
for the Own Risk and Solvency Assessment (ORSA).
Put another way, at what point do we expect 50% GDP destruction –
somewhere between 2070 and 2090 depending on how you parameterise
the distribution. It is worth a moment of reflection to consider
what sort of catastrophic chain of events would lead to this level
of economic destruction.
The chances of me living up to 2070 are pretty slim but they are not
zero. I’d be 97 and probably in need of care. I wonder how much care I
can expect in a world that’s crashing like that.
I’m still enjoying working with Go. I’m still working on Oddµ. Here’s what’s been on my mind this morning: How do I link fediverse accounts to their profile pages? Example: @alex. There are two problems I can see:
international domain names
webfinger
Let’s take a look.
The first problem is the one that I am unable to crack. I think what would need to happen is for me to not look at bytes but do actual UTF-8 decoding. Right now, what I’m doing is this, once I know that we’re looking at an @: skip forward over a-z, A-Z, 0-9, . and - and exactly one other @ and then I skip backwards over . and that this is correct. In the case of the account name above, this makes sure that the . is not included. For international domains, that wouldn’t work. The bytes for schröder.ch would include the bytes 0xc3 and 0xb6 to encode the ö in UTF-8.
I guess I could add “skip forward over any byte that’s above 127” to my list. This would work for schröder.ch but it wouldn’t always end correctly. Does schröder.ch… include the ellipsis? I’d say it does not. It is encoded by the bytes 0xe2, 0x80 and 0xa6 – all of which are bigger than 127. In other words, I need to decode the bytes and then figure out if the bytes are valid characters in a domain name.
This task is nearly impossible to solve. If you want to stare into the abyss, read RFC 5890 or RFC 5892 Appendix B. I think in the context of trusted (big ask!) wiki authors wanting link to a profile page would expect that any letters and digits plus the period and the hyphen are valid domain name constituents.
So what I really need is Unicode categories. I’m guessing a more-or-less valid domain name as entered by a nice person that is copying and pasting a valid account name from somewhere, the rules are: “letters, period and hyphen”? I know you can’t register some of these domains, since the allowed set can vary by registrar and those rules can change over time; and some of the characters are disallowed because they can be used to make phishing attacks; some characters may not be mixed for the same reason; but in our case, those cases don’t matter: if the resulting international resource identifier (IRI) doesn’t point to a valid profile, that’s not a problem.
But of course there are exceptions. 😭
ZERO-WIDTH NON-JOINER “may occur in a formally cursive script (such as Arabic)” … ZERO WIDTH JOINER “may occur in Indic scripts in a consonant-conjunct context” … and so on. It gets very specific! MIDDLE DOT “Between ‘l’ (U+006C) characters only, used to permit the Catalan character ela geminada to be expressed.” – RFC 5982 Appendix A.1
There really is no easy way out.
Perhaps all I can hope for is to improve this slowly, over time.
So the smallest next improvement should be the inclusion of all the Unicode letters. Let’s see how far that gets us.
Ah. Now I remember. I wanted to talk about two things.
The second thing I wanted to talk about is Webfinger. The problem is this: If you have an account like @alex then you can’t just turn that into https://alexschroeder.ch/users/alex. It works most of the time, for sure. The popular servers implement that because Mastodon does it. The form https://alexschroeder.ch/@alex is currently the redirect target of many servers. Sadly, there are servers that support only the first form and not the second, and there are servers that don’t accept either of them.
I am loath to do these lookups because then we get into the topic of cache expiry and cache storage. I could just keep it in memory and rely on the occasional restarts to clear the cache as a first step. And then start to expire cache entries older than a few days at a later stage. I can’t say I like it.
I also don’t want to do this while rendering a page because the page loading will be slow every time you have a new account name. So what I’m doing right now is that I use the guess I mentioned above, an URI of the form https://alexschroeder.ch/users/alex – and I start a background process to do the lookup. The lookup then populates a map of accounts to URIs and overwrites the guess. A subsequent reload of the page then has the improved URI.
On a small site without a lot of traffic, I think it’s OK not to persist this map. Restarts automatically clear the map.
Let’s see if this works. This page has an account where the guess is wrong: https://alexschroeder.ch/users/alex doesn’t exist. But within a few seconds, this should be fixed.
I just published the latest stable release of the Modus
themes. The change log
entry is reproduced further below. For any questions, feel welcome to
contact me.
I will soon install the changes in emacs.git so please wait a little
longer for the updates to trickle down to you.
Package name (GNU ELPA): modus-themes (also built into Emacs 28+)
Backronym: My Old Display Unexpectedly Sharpened … themes
All themes except the tritanopia ones have a new hover colour
The previous colour was not sufficiently distinct from what each theme
defines for the bg-completion palette entry (preview a palette with
M-x modus-themes-preview-colors or M-x modus-themes-preview-colors-current).
This would make it hard to spot the difference while, for example,
using vertico-mode in tandem with vertico-mouse-mode.
Same principle for the difference between the mouse hover and lazy
isearch highlights (e.g. in Dired or Occur buffers).
Changing the hue here follows the same principle that underpinned the
redesign of the grey backgrounds for version 4 of the project:
depending on hardware capabilities, colour reproduction may not be
optimal, so we need to be more considerate with the choice of colour
values, erring on the side of caution.
The modus-operandi-tritanopia and modus-vivendi-tritanopia themes
are not affected by this initiative, as they already used highly distinct hues.
Thanks to Daniel Mendler for bringing this matter to my attention and
for testing the proposed alternatives. This was done via a private
channel and the information is shared with permission. Daniel is the
developer of vertico, among many other excellent packages:
https://github.com/minad
Japanese holidays have the expected style
Japanese calendars style Saturdays uniquely and the Modus themes now
do the same for those who use the japanese-holidays package.
Saturdays show up in a blue colour (which changes to cyan for the
modus-operandi-tritanopia, modus-vivendi-tritanopia themes).
Each theme’s palette has a new semantic colour mapping called
date-holiday-other, just in case we ever encounter another scenario
like this one (users can override any entry in the palette—consult
the manual for the technicalities).
Thanks to Olaf Meeuwissen for bringing this package to my attention
and showing me how traditional Japanese calendars style Saturdays.
This was done in issue 311 on the GitLab mirror:
https://gitlab.com/protesilaos/modus-themes/-/issues/311.
Each theme has semantic colour mappings for terminal emulators
These are used by ansi-term, vterm, and the like. The idea is to
empower users to differentiate background and foreground values,
should they ever encounter a need to do so (when in doubt, do
nothing).
By convention, terminal emulators use the same value for both
background and foreground, although this is not optimal with high
contrast themes because what works as a foreground does not
necessarily look nice as a background.
The default values of the new mappings retain the prior state, just to
not break existing configurations. Consider this a tacit user option
for those who really need it.
All theme definitions conform with the latest standard for metadata
Themes are expected to declare their background type and affinity,
such that the built-in command theme-choose-variant can do what it
describes (switch between related themes). I was already doing this,
though I had to make some adjustments. This is in response to Emacs
bug#65468: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=65468.
Thanks to Mauro Aranda for bringing the matter to my attention.
Proper colours for the inline preview of the corfu top candidate
The corfu-candidate-overlay package is used in tandem with the
corfu package to create an inline preview of the first matching
candidate. Thanks to Nicolas Semrau for bringing this matter to my
attention in issue 89 on the GitHub mirror:
https://github.com/protesilaos/modus-themes/issues/89.
Context indicators for the mode-line or header-line (breadcrumb.el)
I added support for the new breadcrumb package by João Távora. It
displays information about where we are in the given file, such as
under which Org heading or inside which function. The indicator can
be shown on the mode-line or the header-line. Either way, it will now
be legible and consistent with its surroundings.
The new family of nerd-icons is covered by the themes
This is a new set of packages: nerd-icons, nerd-icons-completion,
~nerd-icons-dired, nerd-icons-ibuffer. A popular package that uses
the Nerd icons is doom-modeline, which the themes support as well.
All icons look as intended again
Some faces from the all-the-icons package were not configured
because I accidentally changed their name from something like
all-the-icons-red-alt to all-the-icons-red-warmer. I did that
while renaming the colours defined in each theme’s palette, to make
them more meaningful (“warmer red” can hint at something whereas “alt
red” is more abstract).
Corrected the documentation about custom Org faces
The Modus themes manual shows how to configure arbitrary TODO keywords
to inherit the style of an arbitrary face (get the list of faces
present in your Emacs with M-x list-faces-display). The previous
value I used was faulty. It now is as intended. Thanks to
soaringbird for reporting the issue on the mailing list:
https://lists.sr.ht/~protesilaos/modus-themes/%3CNXPVkVk--3-9%40tuta.io%3E.
The colours used by avy are better for users with tritanopia
I changed the sequence of colours displayed by commands such as
avy-goto-char-timer such that each individual background does not
blend with the ones adjacent to it, while respecting the overall needs
of a tritanopia-friendly design. I also tweaked the colour values to
achieve the desired result. The backgrounds remain distinct from
their context but now also work harmoniously together.
The bg-dim palette entry is marginally brighter in all Modus operandi variants
After extensive testing and side-by-side comparisons, I have concluded
that the marginal increase in brightness improves the affected
interfaces.
The bg-dim background is used, among others, in the header-line, the
popup of the company and corfu packages, as well as the Org source
blocks (when the user option modus-themes-org-blocks is configured
appropriately).
The “intense” palette override preset has new colours for tables and prose metadata
This concerns the modus-themes-preset-overrides-intense (refer to
its documentation on how to use it). The primary target of these
changes is Org mode and the overall effect is subtle. The previous
colours did not combine nicely with all structural elements. For
example, Org clocktables would obscure timestamps by being the same
colour as them, while the table formula would not stand out. These
styles did not fit into the concept of “intense” colours.
The “warmer” palette override preset has more legible strings
The modus-themes-preset-overrides-warmer uses a more prominent warm
green value for strings in programming modes. The effect is subtle,
though it fits in better with the overall aesthetic of these palette
overrides.
Org document keywords like #+author are optionally monospaced
When the user option modus-themes-mixed-fonts is non-nil, all Org
document keywords will be rendered with the fixed-pitch face. This
ensures consistency between keywords such as #+author and “meta
lines” like #+texinfo. Thanks to user fluentpwn for the change: it
is one line and thus the author does not need to assign copyright to
the Free Software Foundation.
Git commit summary lines have a more precise style
This concerns the first line in a Git commit message, as seen while
working with the magit package. Same principle for the log-edit
buffer used by the built-in VC framework. Before, I was hardcoding a
blue colour value, whereas now I apply the success face. The
success face is designed to contrast with the warning face that is
used to show overlong summaries. Furthermore, the added indirection
makes it possible to particularise the colour value, which I do for
the tritanopia themes that cannot use blue.
Miscellaneous
Removed explicit support for the built-in css-mode. Its default
faces are decent. They inherit from standard font-lock faces that
the themes already cover.
Recalibrated wordwise (“refined”) diffs for deuteranopia. The
modus-operandi-deuteranopia and modus-vivendi-deuteranopia have
a little bit more intense colour values applied to wordwise, else
“refined”, diffs. These concern removed lines. The effect is
visible while using magit or the built-in diff-mode.
Backported emacs.git commit 4cf33b6bd02b868ebbf112da7926d7c3c64517ce.
It removed the space from the front matter of the modus-themes.org
file (i.e. the manual) because the Org export did not produce the
right results, per Emacs bug#64548. Thanks to Stephen Berman for
reporting the issue and making the requisite change.
Fixed a typo in the modus-themes-preset-overrides-intense doc
string. Thanks to Nicolas Semrau for bringing this matter to my
attention. It was done in issue 90 on the GitHub mirror:
https://github.com/protesilaos/modus-themes/issues/90.
Made all commands that prompt for a theme (modus-themes-select,
modus-themes-preview-colors) apply the theme category to the
available candidates. This allows the user to target said category
to affect the relevant functions. For example, to set completion
styles with completion-category-overrides or to define a custom
annotation function with the marginalia package.
Added support for new appt-notification face (Emacs 30). Change
upstream by me.
Those of you who follow my blog know that one of my hobbies is translating subtitles. The main reason I do this is to watch stuff with my daughter, who doesn’t yet speak English fluently. Some time ago it dawned on me that I can use my translations twice. Not only can I watch films and tv series with her, but I can use them to help her learn English.
For decades I’ve used view mode in Emacs akin to the command line tool less (or its older inspiration, more) where I page through a file using the spacebar in read-only fashion, and navigating through said file with basic motion commands. A recent video on motion from Emacs Elements has gotten me to indirectly reconsider how motion works in view mode which I’ll share in this post.
That video suggested to rebind the key sequences C-v and M-v to heading (or structural) navigation rather than typically scrolling up or down a screen. After trying it for both Org and Markdown modes, I became convinced. But when viewing said files using C-v, M-v to navigate structure, it seemed clumsy. Wasn’t there already a binding convention for quickly navigating structure in read-only buffers? Turns out there is. Many Emacs packages bind n and p to navigation in read-only buffers (e.g. dired, magit, Org Agenda). So I was led to do this with view mode.
Because view mode is a mode (albeit a minor one), it follows the conventions of having a mode map (view-mode-map) and a mode hook (view-mode-hook). The code below customizes view mode to support bindings of n and p that are specific to the major mode it is applied to. If no major mode is specified, then p and n are mapped to scroll-down-command and scroll-up-command respectively.
The cond expression above can be extended to support more modes as desired.
Highlight Line Mode
With structural navigation in view mode, I find it more useful to highlight the line where the point is. hl-line-mode does just that. The following code shows how to turn on highlight line mode and to disable it upon exiting view mode to edit the file.
1234567
(add-hook'view-mode-hook'hl-line-mode)(defuncc/view-exit()"Advice function to disable highlighting upon exiting view-mode."(hl-line-mode-1))(advice-add'View-exit:after#'cc/view-exit)
One might argue that the above behavior skirts around modal-style editing and navigation, but it’s really not my intention to go in that direction. That said, since using view mode means you are not editing a file, it seems sensible to take advantage of using single key bindings.
Having been around since Emacs version 16, view mode is both old and for me, overlooked with regards to thinking about customizing despite my frequent use of it. Using structural navigation in view mode has been surprisingly delightful. If this is new to you, I think you’ll find it the same.
I'm leaving our next elisp project for when I get back from my
vacation but realized I could make a short video with some
preliminaries - some elisp tools that we'll need that we can get out
of the way.
Specifically, elisp's built in data structures.
We've already used the list data structure and in this video we go a
bit more in depth.
Lists are great for, well, list processing but they're not great for
random access. For that we can use vectors. Think of them like arrays
in other languages but with a functional way of accessing:
(setq v [1020304050]) ;; define a vector (aref v 1) ;; returns the 20, the value in index 1 (aset v 1100) ;; stores 100 in index i;; the vector will now have [10 100 30 40 50] (mapcar#'1+ v)
;; the above returns a new list with (2 101 31 41 51)
Note in that last example above we can use mapcar which we covered in
an earlier video on vectors as well as lists but the return is a new
list, not a vector.
We also cover elisp hash tables which are similar to python
dictionaries. With them you can store and retrieve values based on
keys (key value pairs). We're not actually going to use them in the
next project so you can see the video for details.
The data structure we will be using are association lists. You can
think of them as a lightweight hash table. Basically a list of
pairs. A pair is a special form of a list that only has two items
(well, not exactly but see the video for details). We write them by
putting a period between the two elements - (1 . 2) - that's a pair
with 1 as the first part and 2 as the second.
There was another meeting a couple of weeks ago of EmacsATX, the Austin Emacs Meetup group. For this month we had no predetermined topic. However, as always, there were mentions of many modes, packages, technologies and websites, some of which I had never heard of before, and some of this may be of interest to ... Read more
For some time there’s been a problem between Gnupg 2.4.1 and Emacs. Although you can decrypt files with Emacs—your .authinfo.gpg file for example—you couldn’t edit and save an encrypted file: Emacs hangs. This turns out to be a mismatch between the way Emacs and Gnupg expect to communicate the password and apparently it’s a hard problem to resolve. Until one or both apps are fixed you need a workaround.
The workaround is simple: downgrade to Gnupg 2.4.0. That’s not hard, of course, if you compile Gnupg yourself, but is harder if you use Brew to install it. There’s no easy way to do that with the standard Brew commands and the process that I’ve seen previously seemed a bit complex.
Álvaro Ramírez to the rescue. He has a post that gives us a simple procedure for doing the downgrade. Basically, you just download the Brew recipe and then install it using standard Brew commands. I’ve already done that and it took me less than a minute so there’s no reason not to follow his prescription if you’re having the problem.
The first image of Marilyn Monroe I have ever seen is a photo of her, up on the
wall of the living room in the house where I grew up. It has been there for as
long as I can remember and it is still there today. I have never paid too much
attention to it, to be honest, even when I understood who the charming lady was.
It seemed like an icon from a past I did not feel really attached to. Recently,
while reading Joyce Carol Oates’ Blonde and right before watching Andrew
Dominik’s film of the same name, I have had the chance to look at the photo more
closely. Once again, it eluded me. Where is Norma Jeane here?
Oates and Dominik ask themselves the same question and set out to explore what
phrasing it means, way before searching for an answer. What Oates aptly
discovers and what Dominik bravely takes away from her book is that in order to
understand the woman behind Marilyn Monroe one has to start from her body and
think of how she lived it. This could explain why Oates’ and Dominik’s works
have been met with mixed reactions. There is no point in expecting the story of
Marilyn Monroe or asking for another apologetic tale of belated repentance, just
as it makes no sense to complain about how shattered one’s own mental picture of
her will be after the last word and the last scene. Quite the contrary. The
fragments Oates and Dominik disperse should be treasured as pieces of a bigger
puzzle everyone is required to complete.
Marilyn Monroe (Cecil Beaton, 1956)
Indeed, Marilyn Monroe remains a mystery the solution of which cannot be found
neither in Oates’ book nor in Dominik’s film. Still, the pages and the images
provide a specific point of view, an angle from which one can observe Marilyn
Monroe and try for themselves to find a way in her world of mirrors,
illusions, fame and failure, tragedy and eternity. I have the utmost respect for
a writer, a director or, generally speaking, an artist who provokes me or even
infuriates me. As long as a reaction to what is placed in front of me is
demanded, art is already doing the right thing regardless of any aesthetic
consideration. I like to think I deserve a bit of credit from an artist, I want
to believe they know they can rely on me doing my best to engage with their
effort. Simply put, I may not share the artist knowledgeable background, but it
does not mean that I should be treated as a goof.
This is why I felt so compelled by Oates and Dominik. They refuse to pamper
their audience and keep pushing forward whatever they think worth of our
attention. Take Oates, for instance. She offers a plethora of voices, never at
peace with a simple opinion, deliberately refusing to add details for the sake
of completeness. What if the reader cannot understand a specific passage because
they are not familiar with Norma Jean’s youth? What if they cannot recognise
names, places, events? Oates does not care. She points at the source material
for her book in the very first pages, so the reader knows how to fill the gaps
if so inclined. Eventually, Oates shapes facts, invents characters, exaggerates
situations, and breaks conventional pictures of Marilyn Monroe. What we read is
beyond the true-false dichotomy. Only Norma Jean painfully knows what is true
and what is false about Marilyn Monroe.
Blonde (Andrew Dominik, 2022)
Dominik accepts Oates’ uncompromising imagination and dives as deep as possible
in the horror of this story. The director plays to great extent with Norma Jean
being an avatar of Marilyn Monroe. One would expect the exact opposite, but
Dominik convincingly insists on the psychological power of Monroe, a white men
product, over the fragile woman who was never able to overcome it. Monroe takes
apart Norma Jean mind and body, destroys her private life every time she tries
to get it back, and owns her to the end until exhaustion. Much like Oates,
Dominik picks what makes sense to him from the book and, to a larger extent,
from Monroe’s life. And just like the writer, the director twists specific
moments and develops new scenarios with remarkable effect. Look, for example, at
his shocking depiction of Norma Jean’s nightmares or marvel at the outstanding
yet unsustainable last minute of the film.
Both authors have been criticised for their supposedly merciless treatment of a
sacred icon like Marilyn Monroe. Again, destabilising common conscience and
subverting given myths is precisely what art should do. Oates’ and Dominik’s
works aim at the same target: where does entertainment end? More than twenty
years separate the film from the book, but this question is as relevant today as
it was back in the early 2000s.
Please note that planet.emacslife.com aggregates blogs, and blog authors might mention or link to nonfree things. To add a feed to this page, please e-mail the RSS or ATOM feed URL to sacha@sachachua.com . Thank you!