While collecting posts for Emacs News, I came
across this question about adding up Org Mode
table data by tag hierarchy, which might be
interesting if you want to add things up in
different combinations. I haven't needed to do
something like that myself, but I got curious
about it. It turns out that you can define a tag
hierarchy like this:
The first two lines remove any other tags you've
defined in your config aside from those in
org-tag-persistent-alist, but can be omitted if
you want to also include other tags you've defined
in org-tag-alist. Note that it doesn't have to
be a strict tree. Tags can belong to more than one
tag group.
EduMerco wanted to know how to use those tag
groups to sum up rows in a table. I added a
#+NAME header to the table so that I could refer
to it with :var source=source later on.
(defunmy-sum-tag-groups (source &optional groups)
"Sum up the rows in SOURCE by GROUPS.If GROUPS is nil, use `org-tag-groups-alist'."
(setq groups (or groups org-tag-groups-alist))
(cons
(car source)
(mapcar
(lambda (tag-group)
(let ((tags (org--tags-expand-group (list (car tag-group))
groups nil)))
(cons (car tag-group)
(seq-map-indexed
(lambda (colname i)
(apply '+
(mapcar (lambda (tag)
(let ((val (or (elt (assoc-default tag source) i) "0")))
(if (stringp val)
(string-to-number val)
(or val 0))))
tags)))
(cdr (car source))))))
groups)))
Then that can be used with the following code:
#+begin_src emacs-lisp :var source=source :colnames no :results table(my-sum-tag-groups source)#+end_src
to result in:
tag
Q1
Q2
GT1
10
9
GT2
4
8
GT3
5
11
Because org--tags-expand-group takes the groups
as a parameter, you could use it to sum things by
different groups. The #+TAGS: directives above set
org-tag-groups-alist to:
I haven't specifically needed to add tag groups in tables myself, but I suspect the recursive expansion in org--tags-expand-group might come in handy even in a non-Org context. Hmm…
I messed up on one of my tax forms, so I needed to
send the tax agency a single document that
included the amended tax return and the supporting
slips, with my name, social insurance number, and
reference number on every page. It turned out to
be rather complicated trying to get calculated
\pageref to work with \includepdf, so I just
used \hyperlink with hard-coded page numbers. I
also needed to use qpdf --decrypt input.pdf
output.pdf to decrypt a PDF I downloaded from one
of my banks before I could include it with
\includepdf.
Here's what I wanted to do with this Org Mode / LaTeX example:
Coloured header on all pages with info and page
numbers
Including PDFs
Hyperlinks to specific pages
* Letter#+DATE:2025-09-24#+LATEX_CLASS: letter#+OPTIONS: toc:nil ^:nil title:nil#+LATEX_HEADER: \usepackage[margin=1in]{geometry}#+LATEX_HEADER: \hypersetup{hidelinks}#+LATEX_HEADER: \usepackage{pdfpages}#+LATEX_HEADER: \usepackage{fancyhdr}#+LATEX_HEADER: \usepackage{lastpage}#+LATEX_HEADER: \usepackage{xcolor}#+LATEX_HEADER: \signature{FULL NAME GOES HERE}#+LATEX_HEADER: \fancypagestyle{plain}{#+LATEX_HEADER: \fancyhf{}#+LATEX_HEADER: \fancyhead[L]{\color{teal}\hyperlink{page.1}{HEADER INFO}}#+LATEX_HEADER: \fancyhead[R]{\color{teal}\thepage\ of \pageref{LastPage}}#+LATEX_HEADER: }#+LATEX_HEADER: \pagestyle{plain}#+LATEX_HEADER: \makeatletter#+LATEX_HEADER: \let\ps@empty\ps@plain#+LATEX_HEADER: \let\ps@firstpage\ps@plain#+LATEX_HEADER: \makeatother#+LATEX_HEADER: \renewcommand{\headrulewidth}{0pt}#+LATEX_HEADER: \newcommand{\pdf}[1]{\includepdf[link,pages=-, scale=.8]{#1}}#+LATEX_HEADER: \newcommand{\pages}[2]{\hyperlink{page.#1}{#1}-\hyperlink{page.#2}{#2}}#+LATEX: \begin{letter}{}#+LATEX: \opening{Dear person I am writing to:}
Text of the letter goes here.
Please find attached:
| Pages | || @@latex:\pages{2}{10}@@ | Description of filename1.pdf || @@latex:\hyperlink{page.5}{5}@@ | Can link to a specific page || @@latex:\pages{11}{15}@@ | Description of filename2.pdf |#+LATEX:\closing{Best regards,}#+LATEX: \end{letter}#+LATEX: \pdf{filename1.pdf}#+LATEX: \pdf{filename2.pdf}
After filling it in, I exported it with C-c C-e
(org-export) C-s (to limit it to the subtree)
l p (to export a PDF via LaTeX).
Not the end of the world. At least I learned a
little more LaTeX and Org Mode along the way!
at most one sentence per line (although still wrapping at fill-column)
at most one sentence per line (don't even try to keep it within fill-column).
Screencast cycling through different paragraph formats
Now that semantic linefeeds are part of core Emacs (as of 2025-06-14), the code for cycling through different paragraph formats can be pretty short. Most of it is actually just the logic for cycling through different commands. That might come in handy elsewhere. There's an unfill package as well, but since the code for unfilling a paragraph is very simple, I'll just include that part.
Note that fill-paragraph-semlf pays attention to sentence-end-double-space, and it doesn't handle comments yet. I also have some code to check if I'm in a comment and skip those filling methods if needed.
This might encourage me to write shorter sentences.
I can move sentences around with M-Shift-up and M-Shift-down in Org Mode, which is pretty handy.
Also, one sentence per line makes diffs easier to read.
But wrapped text is annoying to edit in Orgzly Revived on my phone, because the wrapping makes a very ragged edge on a narrow screen.
I might unwrap things that I want to edit there.
With a little bit of tweaking to skip source blocks, I can narrow to the subtree, select my whole buffer, and cycle the formatting however I like.
(defvarmy-repeat-counter'()
"How often `my-repeat-next' was called in a row using the same command.This is an alist of (cat count list) so we can use it for different functions.")
(defunmy-unfill-paragraph ()
"Replace newline chars in current paragraph by single spaces.This command does the inverse of `fill-paragraph'."
(interactive)
(let ((fill-column most-positive-fixnum))
(fill-paragraph)))
(defunmy-fill-paragraph-semlf-long ()
(interactive)
(let ((fill-column most-positive-fixnum))
(fill-paragraph-semlf)))
(defunmy-repeat-next (category &optional element-list reset)
"Return the next element for CATEGORY.Initialize with ELEMENT-LIST if this is the first time."
(let* ((counter
(or (assoc category my-repeat-counter)
(progn
(push (list category -1 element-list)
my-repeat-counter)
(assoc category my-repeat-counter)))))
(setf (elt (cdr counter) 0)
(mod
(if reset 0 (1+ (elt (cdr counter) 0)))
(length (elt (cdr counter) 1))))
(elt (elt (cdr counter) 1) (elt (cdr counter) 0))))
(defunmy-in-prefixed-comment-p ()
(or (member 'font-lock-comment-delimiter-face (face-at-point nil t))
(member 'font-lock-comment-face (face-at-point nil t))
(save-excursion
(beginning-of-line)
(comment-search-forward (line-end-position) t))))
;; It might be nice to figure out what state we're;; in and then cycle to the next one if we're just;; working with a single paragraph. In the;; meantime, just going by repeats is fine.
(defunmy-reformat-paragraph-or-region ()
"Cycles the paragraph between three states: filled/unfilled/fill-sentences.If a region is selected, handle all paragraphs within that region."
(interactive)
(let ((func (my-repeat-next 'my-reformat-paragraph'(fill-paragraph my-unfill-paragraph fill-paragraph-semlf
my-fill-paragraph-semlf-long)
(not (eq this-command last-command))))
(deactivate-mark nil))
(if (region-active-p)
(save-restriction
(save-excursion
(narrow-to-region (region-beginning) (region-end))
(goto-char (point-min))
(while (not (eobp))
(skip-syntax-forward " ")
(let ((elem (and (derived-mode-p 'org-mode)
(org-element-context))))
(cond
((eq (org-element-type elem) 'headline)
(org-forward-paragraph))
((member (org-element-type elem)
'(src-block export-block headline property-drawer))
(goto-char
(org-element-end (org-element-context))))
(t
(funcall func)
(if fill-forward-paragraph-function
(funcall fill-forward-paragraph-function)
(forward-paragraph))))))))
(save-excursion
(move-to-left-margin)
(funcall func)))))
(keymap-global-set "M-q"#'my-reformat-paragraph-or-region)
Sometimes I use writeroom-mode to make the lines look even narrower, with lots of margin on the side.
: Changed my mind, I want the clipboard URL to be used by default. More bugfixes.
: Fix bug in my-page-title. Add mastodon-toot-mode-map.
I love so many things about Org Mode's links. I can use C-c C-l (org-insert-link) to insert a link. If I've selected some text, C-c C-l turns the text into the link's description. I can define my own custom link types with interactive completion, default descriptions, and export formats. This is so nice, I want it in all the different places I write links in:
Markdown, like on the EmacsConf wiki; then I don't have to remember Markdown's syntax for links
If I haven't already selected some text, I want to use the page title or the custom link description as a default description.
I want to be able to use my custom link types for completion, but I want it to insert the external web links if I'm putting the link into a non-Org Mode buffer (or in a source or export block that isn't Org Mode). For example, let's say I select dotemacs:my-org-insert-link-dwim with completion. In Org Mode, it should use that as the link target so that I can follow the link to my config and have it exported as an HTML link. In Markdown, it should be inserted as [Adding Org Mode niceties elsewhere: my-org-insert-link-dwim](https://sachachua.com/dotemacs#my-org-insert-link-dwim).
Mostly, this is motivated by my annoyance with having to work with different link syntaxes:
HTML
<a href="https://example.com">title</a>
Org
[[https://example.com][title]]
Plain text
title https://example.com
Markdown
[https://example.com](title)
Oddmuse
[https://example.com title]
I want things to Just Work.
Screencast showing how I insert links
Play by play:
0:00:00 inserting a custom dotemacs link with completion
0:00:11 inserting a link to a blog post
0:00:28 selecting text in an HTML export block and adding a link to it
0:00:48 adding a bookmark link as a plain text link in a Python src block
All right, let's dig into the details. This code
gets the page title so that we can use it as the
link's description. I like to simplify some page
titles. For example, when I link to Reddit or HN
discussions, I just want to use "Reddit" or "HN".
Now I want an Emacs Lisp function that interactively reads a link with completion, but doesn't actually insert it. I extracted this logic from org-read-link.
my-org-read-link, extracted from org-read-link
(defunmy-org-read-link (&optional default)
"Act like `org-insert-link'. Return link."
(let* ((wcf (current-window-configuration))
(origbuf (current-buffer))
(abbrevs org-link-abbrev-alist-local)
(all-prefixes (append (mapcar #'car abbrevs)
(mapcar #'car org-link-abbrev-alist)
(org-link-types)))
link)
(unwind-protect;; Fake a link history, containing the stored links.
(let ((org-link--history
(append (mapcar #'car org-stored-links)
org-link--insert-history)))
(setq link
(org-completing-read
(org-format-prompt "Insert link" (or default (caar org-stored-links)))
(append
(mapcar (lambda (x) (concat x ":")) all-prefixes)
(mapcar #'car org-stored-links)
;; Allow description completion. Avoid "nil" option;; in the case of `completing-read-default' when;; some links have no description.
(delq nil (mapcar 'cadr org-stored-links)))
nil nil nil
'org-link--history
(or default (caar org-stored-links))))
(unless (org-string-nw-p link) (user-error"No link selected"))
(dolist (l org-stored-links)
(when (equal link (cadr l))
(setq link (car l))))
(when (or (member link all-prefixes)
(and (equal ":" (substring link -1))
(member (substring link 0 -1) all-prefixes)
(setq link (substring link 0 -1))))
(setq link (with-current-buffer origbuf
(org-link--try-special-completion link)))))
(when-let* ((window (get-buffer-window "*Org Links*" t)))
(quit-window 'kill window))
(set-window-configuration wcf)
(when (get-buffer "*Org Links*")
(kill-buffer "*Org Links*")))
link))
So now the my-org-insert-link-dwim function can read a link with completion (unless I'm getting it from the clipboard), get the default description from the link (using custom links' :insert-description or the webpage's title), and either wrap the link around the region or insert it in whatever syntax makes sense.
On a related note, you might also enjoy:
Bookmarks - my custom link type for bookmark: that offers completion from resources.org
The Emacs Carnival theme for September is obscure
packages, which made me think of how the
backup-walker package saved me from having to
write some code all over again. Something went
wrong when I was editing my config in Org Mode. I
probably accidentally deleted a subtree due to
over-enthusiastic speed commands. (… Maybe I
should make my k shortcut for
my-org-cut-subtree-or-list-item only work in my
Inbox.org and news.org files.) Chunks of my
literate Emacs configuration were gone, including
the code that defined my-org-insert-link-dwim.
Before I noticed, I'd already exported my (now
slightly shorter) Emacs configuration file with
org-babel-tangle and restarted Emacs. I couldn't
recover the definition from memory using
symbol-function. I couldn't use vundo to browse
the Emacs undo tree. As usual, I'd been neglecting
to commit my config changes to Git, so I couldn't
restore a previous version. Oops.
Well, not the first time I've needed to rewrite
code from scratch because of a brain hiccup. I
started to reimplement the function. Then I
remembered that I had other backups. I have a 2 TB
SSD in my laptop, and I had configured Emacs to
neatly save numbered backups in a separate
directory, keeping all the versions without
deleting any of the old ones.
At the moment, there are about 12,633 files adding
up to 3 GB. Totally worth it for peace of
mind. I could probably use grep to search for the
function, but it wasn't easy to see what changed
between versions.
I had learned about backup-walker in the process
of writing about Thinking about time travel with
the Emacs text editor, Org Mode, and backups. So I
used backup-walker to flip through my file's
numbered backups in much the same way that
git-timemachine lets you flip through Git versions
of a file. After M-x backup-walker-start, I
tapped p to go through the previous backups. The
diff it showed me made it easy to check with C-s
(isearch-forward) if this was the version I was
looking for. When I found the change, I pressed
RET to load the version with the function in it.
Once I found it, it was easy to restore that
section. I also restored a couple of other
sections that I'd accidentally deleted too, like
the custom plain text publishing backend I use to
export Emacs News with less punctuation. It took
maybe 5 minutes to figure this out. Hooray for
backup-walker!
Note that the backup-walker diff was the other way
around from what I expected. It goes "diff new
old" instead of "diff old new", so the green
regions marked with + indicate stuff that was
removed by the newer version (compared to the
one a little older than it) and the red regions
marked with - indicate stuff that was added.
This could be useful if you think backwards in
time, kind of like the Emacs Antinews file, but my
mind doesn't quite work that way. I wanted it to
look like a regular diff, with the additions in
newer versions marked with +. Emacs being Emacs,
I changed it. Here's an example showing what it
looks like now:
Figure 1: backup-walker diffs going the direction I want them to: additions (+) marked in green, deletions (-) in red
The following code makes it behave the way I
expect:
backup-walker is not actually a real package in
the sense of M-x package-install, but
fortunately, recent Emacs makes it easier to
install from a repository. I needed to
install it from
https://github.com/lewang/backup-walker. It was
written so long ago that I needed to
defalias some functions that were removed in
Emacs 26.1. Here's the use-package snippet from my
configuration:
So there's an obscure package recommendation:
backup-walker. It hasn't been updated for more
than a decade, and it's not even installable the
regular way, but it's still handy.
I can imagine all sorts of ways this workflow
could be even better. It might be nice to dust off
backup-walker off, switch out the obsolete
functions, add an option for the diff direction,
and maybe sort things out so that you can reverse
the diff, split hunks, and apply hunks to your
original file. And maybe a way to walk the backup
history for changes in a specific region? I
suppose someone could make a spiffy
Transient-based user interface to modernize it.
But it's fine, it works. Maybe there's a more
modern equivalent, but I didn't see anything in a
quick search of M-x list-packages/ N
(package-menu-filter-by-name-or-description) for
"backup~, except maybe vc-backup.1 Is there a general-purpose VC equivalent to
git-timemachine? That might be useful.
I should really be saving things in proper version
control, but this was a good backup. That reminds
me: I should backup my backup backups. I had
initially excluded my ~/.config directory from
borgbackup because of the extra bits and bobs that
I wouldn't need when restoring from backup (like
all the Emacs packages I'd just re-download). But
my file backups… Yeah, that's worth it. I
changed my --exclude-from to --patterns-from
and changing my borg-patterns file to look like
this:
+ /home/sacha/.config/emacs/backups
- /home/sacha/.config/*
# ... other rules
vc-backup: The original
repo is missing, but you can read it via ELPA's
copy. Update: It's over on Codeberg now, and presumably the info on ELPA will be updated soon.