Emacs Blogging: Insert Tag from YAML Frontmatter Posts

My blog posts here usually have a line like:

tags: [ swift, xcode, codesigning ]

For tags I don’t use a lot, I sometimes don’t remember how to write them. So I do the only sane thing – and go to my website’s list of tags and look for a match.

Got annoyed by that now after a couple of years :)

Extract All Tags from All Posts

Using rg, I can match these lines and focus on group matches inside without piping the output through sed or similar.

The regular expression that works good enough for me:

^tags:\s\[([a-zA-Z0-9 -_]+)\]

The first group here then returns " swift, xcode, codesigning " for the example above with the right ripgrep incantation:

$ rg --context 0 \
     --no-filename \
     --no-heading \
     --replace "\$1" \
     -- <<regex here>>

This produces just the string inside the tags: [...] brackets, without filename, and no empty lines in between.

Here’s the Emacs Lisp version to get these lines:

(defun ct/all-tag-lines ()
  "Extract the array contents of YAML lines for `tags: [...]'."
  (let ((project-dir (cdr (project-current)))
        (regex "^tags:\\s\\[\\s*([a-zA-Z0-9 -_]+)\\s*\\]"))
    (shell-command-to-string
     (concat "rg --context 0 --no-filename --no-heading --replace \"\\$1\" -- " (shell-quote-argument regex) " " project-dir))))

Now time to clean this up and make this usable.

Transform the Extracted YAML Lines Into Filterable Lists in Emacs

Samples output from rg is e.g.:

zettelkasten, reading, archive
calendarpasteapp
zettelkasten, writing, personal, craft
nv, zettelkasten, software, review
writing, productivity, quantified-self

I need to split the lines into individual tags and then remove duplicates like zettelkasten.

  • Split string by lines, trimming whitespace:

      (split-string "..." "\n" nil " ")
    
  • Split lines by comma and/or spaces to extract individual tags, dropping empty strings:

      (split-string "..." "[, ]+" t " ")
    
  • Combined:

      (split-string "..." "[, \n]+" t " \n")
    

To delete duplicates, delete-dups does the trick:

(defun ct/all-tags ()
  "Return a list of unique tags across all articles."
  (delete-dups
   (split-string (ct/all-tag-lines) "[, \n]+" t " \n")))

This returns an (unsorted) list of unique tag strings.

zettelkasten
reading
archive
calendarpasteapp
writing
personal
craft
nv
software
review
productivity
quantified-self

With that, I’m almost finished. I can pass this to completing-read to get – well, the name reveals almost as much: – interactive completion for matches in this selection.

And I ultimately want to insert the match, not just produce a result programmatically.

So this is the “public”, i.e. user facing function I’m using:

(defun ct/insert-project-tag ()
  "Select and insert a tag from YAML frontmatter tags in the project."
  (interactive)
  (insert (completing-read "Tag: " (ct/all-tags))))

Since I’m using the built-in project.el package, I added a key binding to C-x p t (am actually using SPC p t in command mode) to insert a tag:

(define-key project-prefix-map (kbd "t") #'ct/insert-project-tag)
Ah well, looks like I have singular/plural form duplicates already. Gah.

Up next, I’d maybe like to push this completion from a selection in the mode-line to completion-at-point, i.e. to get suggestions and auto-completion in-place while I type.

Judging by the speed I implemented these things in the past, it should be ready by 2026.

Disambiguate Code Signing Identities

I’ve recently added the code signing identities for a client to my dev Mac. Some old code signing scripts then failed to run:

🛑 Apple Development: ambiguous (matches “Apple Development: Christian Tietze (xxxxxxxxxx)” and “Apple Development: Christian Tietze (xxxxxxxxxx)” in /Users/myuser/Library/Keychains/login.keychain-db)

Never encountered that before, to be frank.

Turned out that the script was using the $CODE_SIGN_IDENTITY environment variable, which is just the string “Apple Development”. ⌘F looking through the other environment variables at my disposal, I found $EXPANDED_CODE_SIGN_IDENTITY, which is a hash, and $EXPANDED_CODE_SIGN_IDENTITY_NAME, which is the expanded form that the error message quoted above uses.

I tried both, and both work. The codesign man pages say as much: either provide a partial matching string, which only works if there’s just one match, or a complete form, or 4 characters of a hash. The $EXPANDED_CODE_SIGN_IDENTITY is more than 4 characters long, but that’s looking like the hash.

The failing script must be pretty ancient, because all my notes with copypasta inside that involve code signing use $EXPANDED_CODE_SIGN_IDENTITY already.

New Library Pages

I’ve added a new section to this site. Wanted to call it /books, but that’s taken and about my books :)

So I added the new /library index. It shows books added to my personal library sorted by year I read them.

At the moment, there’re only these three, all of which I needed (wanted) for the previous post:

This collection is bound to grow. I missed something like this for a couple of years. An overview of posts about book would’ve been nice for lots of reasons.

Now the basics are prepared.

TDD Is About Writing Counter-Factual Statements

Writing unit tests first, in the manner of test-driven development, is some kind of “wishful programming”: you create a counter-factual situation in the test code, e.g. one that uses types and methods that don’t even exist.

Then you add the implementation to make the counter-factual statements (and failing tests) the actual reality.

You turn a lie into the truth, a thought-experiment into reality.

See also:


I’m not sure what to do with this association, yet. It just came to mind when used the term “wishful programming”, or “wishful thinking”, in a conversation recently.

In the tradition of linguistics and analytical philosophy, “counterfactuals” are a whole subject to study. Since I’ve now made the mental connection from TDD to “counterfactuals”, maybe I can back-port some interesting tid-bits from linguistics to TDD. It’s not very likely – then again, folks figured out how to replicate the good parts of a fringe book on architecture to Design Patterns for programming.

I picked up this term from Gio Lodi’s book, Test-Driven Development in Swift, by the way.

Key Binding to Select Paragraph or Org Element

I used to rely on M-h to mark the structural element in Emacs; in text buffers, that’d be the paragraph, in org buffers, that was the whole outline item (when the cursor was in the heading line, at least).

Ever since I installed Emacs for Mac OS X which now is also on Emacs 28.1, this shortcut wouldn’t work for me anymore, because my Meta key is my left Command key, and Cmd-H is the macOS shortcut to hide the frontmost app.

I’m rather happy about Emacs for Mac OS X’s adherence to default macOS shortcuts.

So I looked for alternative key bindings.

My modal input scheme (Xah Fly Keys) has selection shortcuts on the number keys 6, 7, 8 and 9. But the one on 9 never made sense to me, so I now use that key to select the paragraph or org-element.

My own function that does the selection:

(defun ct/mark-paragraph ()
  "Mark the paragraph or structural element, depending on the mode."
  (interactive)
  (cond
   ((derived-mode-p 'org-mode)
    (org-mark-element))
   ((derived-mode-p 'web-mode)
     (web-mode-mark-and-expand))
   (t
    (mark-paragraph))))

This function is extensible to e.g. select an enclosing HTML element in web-mode or a function or type or other structural element in C code or Swift. I have yet to add these.

Update: Screw it, I just added the HTML variant. Didn’t even know it was bound to C-c RET until I looked for a suitable function.

I’m using derived-mode-p to test for both same modes, and derived modes; so e.g. (derived-mode-p 'outline-mode) will also return non-nil when visiting an org-mode buffer, since org-mode is derived from outline-mode. Got this tip from a post by Bozhidar Batsov.

A better name would maybe be ct/mark-paragraph-dwim because it’s context sensitive and does different things in different modes.

The key binding isn’t interesting, but here it is for completion:

(define-key xah-fly-command-map (kbd "9") #'ct/mark-paragraph)
;; was 'xah-select-text-in-quote

TableFlip v1.4.0 Released

I’ve released an update to existing users of TableFlip last week. It works well so far, so now’s the time for the official announcement of the new version, available for direct download.

Most notable changes:

  • TableFlip now guesses sophistically determines the delimiter in CSV files, if needed. So you can use ; or \t just fine, which means export from Numbers works out of the box.
  • Non-comma-delimiters also “stick” when saving. No auto-replacement to comma anymore.
  • The CSV loading and saving is more robust, especially with quoted content. There’s been issued reported in the past, and these should now be fixed. Thanks for your emails and sample files, folks! ❤️

For my nerdy readers, behind the scenes I also upgraded Sparkle from the XPC branch to the actual Sparkle 2.0 stuff. I sincerely hope it will ingest the upcoming updates, too. (It’d not be the first time I broke the update mechanism of one of my apps, heh.)

What is TableFlip, you ask?

TableFlip is a fast tabular data editor – the ideal companion app for Markdown users who want beautiful tables in their documents. Open the file in both TableFlip and your text editor, and you’re set: TableFlip lets you edit all tables it finds inside your document and updates live as you edit your text.

Well, and you can edit CSV files, too, and export to LaTeX.

TableFlip v1.4.0 is available from the website and the Mac App Store.

DevCleaner for Xcode

Found DevCleaner for Xcode yesterday and gave it a spin. It’s freeware on the Mac App Store with a tip jar.

Developers who ever nuked their Derived Data folder (I have an alias in my .profile for that) know how much stuff Xcode collects over the years. And then there’s outdated iOS Simulator versions nobody needs anymore. And log files, of course, by Xcode.

I’m pleased to say that DevCleaner does a good job at enabling me to select what I want to remove from the set of things that could be obsolete.

I have Xcode 14 beta installed (to my dismay), so DevCleaner suggested all Xcode 13.x related things could go away. It tries to be smart and helpful here, but I kept the Xcode 13.4.1 logs just in case. The option is there.

Where in the past I would occasionally nuke all Derived Data, DevCleaner shows these folders in a sensible way, with a reference to the project location. I usually create throwaway projects to test and debug something in my Downloads folder. The derived data of these projects is absolutely useless after a month, week, or sometimes even a day. Since DevCleaner shows where the original project was located, I’ve been able to delete data for every long-gone project that was in my Downloads folder.

I cannot stress enough how cool I find this.

Deleting derived data folders is often hard to do by name alone: when I download a new version of a 3rd part dependency that I am using in my apps, and open the project in my Downloads folder, and play around with it, then I have two derived data folders for this dependency, but only one is relevant for the actual project. Either I don’t care and delete both when I know building them is trivial, or I don’t bother at all and they stay around forever.

I really, really like this feature.

Would be even better if I could sort or filter by origin path, but it’s ok the way it is.

Mac App Notarization Workflow in 2022

The Notarization API changed a bit, so my old scripts broke. Revisiting the approach, it’s delightfully simple to notarize an app nowadays if you use the Apple toolchain on macOS.

I haven’t watched the WWDC 2022 video on Notarization but did take the documentation link there into account.

The result is this for TableFlip, an app I distribute on my own and via the App Store; note I use Xcode archives for all my apps. Better hackers than me have custom build scripts, of course. Adjust accordingly.

  1. In Xcode, select Product ▶ Archive from the main menu and build your Mac app for release as usual.

  2. When the Organizer window pops up, use the large Distribute App button. For TableFlip, I use it twice:

    • First, distribute to App Store Connect. Everything there’s automated in the upload wizard and, the rest depends on App Store Review. Once that’s out of the way:

    • Second, distribute via “Developer ID”, i.e. sign and export an .app bundle for distribution on your own. Do upload to the notary service.

  3. Wait for the notarization process to finish. (Xcode displays a notification and you get an email.) Then, form the Organizer window, select “Export Notarized App” from the right sidebar.

    In 2022 and Xcode 13, you still need to select a different list item and then re-select the notarized app archive to make the export button re-validate and activate. I sometimes also have to restart Xcode when things go super bad and Xcode just won’t update the status of the archive. Balls.

  4. With the notarized .app exported to disk, I zip this and put the version into the filename. Yesterday, that was TableFlip-v1.4.0.zip. This zip is used to distribute Sparkle updates.

    I do not distribute direct app downloads this way because you get all kinds of trouble with Gatekeeper path randomization and app translocation. Distribution via .dmg solved most of these issues. So zip for Sparkle updates only.

  5. To prepare the downloadable .dmg for the website, I use DropDMG. The disk image contains graphical backgrounds, and DropDMG automatically code-signs the disk image file. Nice. (Note: use APFS when you target macOS 10.13+ for faster mounting speeds. Got this tip from Zorg, who you might know as the current maintainer of Sparkle, via Slack in Nov 2021.)

  6. Finally, I notarize the .dmg, too! Otherwise, users would be warned when opening the .dmg itself, even though the app inside is notarized. Manual notarization also means you need to staple the ticket to the file for distribution; otherwise, users would have to request the ticket from Apple’s servers, and that sounds like an unnecessary point of failure to me.

    • The command line call to sign foo.dmg is of this form:

        $ xcrun notarytool submit \
            foo.dmg --wait \
            --apple-id your-apple-id@example.com \
            --password app-spec-ific-pass \
            --team-id ABC123123123
      

      You can also use keychain instead of passwords, as Rich Siegel kindly pointed out in chat, also mentioning man notarytool for reference. The password-based version works well enough for me, though. Note that you use an app-specific password, not your main Apple ID password. You can generate these from https://appleid.apple.com.

      This new command waits for the notarization to finish, which makes timing of the next step simpler than it used to be:

    • The stapling step is:

        $ xcrun stapler staple foo.dmg
        ...
        The staple and validate action worked!
      
  7. Bonus: you can verify that the stapling worked anytime in the future via:

     $ xcrun stapler validate foo.dmg
     The validate action worked!
    

    There’s no need for this in the process, though, as the stapling call contains a verification step automatically. (At least it tells as much.)

No need to zip these for distribution – DMG files can be created with compression, and DropDMG offers this, too.

My .dmg files then get uploaded to AWS for global distribution (it’s rather generous on traffic compared to my own host).

Users can download the .dmg binary file in their browsers directly. When users open the file, there won’t be a Gatekeeper warning even if the Mac is offline. Dragging the app from the disk image to the Application folder (which you will want to include as an alias in the DMG of course) removes the quarantine xattr flag and solved the app translocation problem from zipped apps opened from e.g. the ~/Downloads folder mentioned above.

It’s the best I’ve got thus far.

Alternatives and other notes for reference:

  • Michael Tsai and Josh Wisenbaker revealed (to me) that you can omit notarization of the .app when the .dmg is notarized, but then there’s all kinds of edge cases once you transfer the .app bundle to another Mac that wasn’t privvy to the disk image’s notarization. The notarization ticket is stapled to the .dmg, not the .app inside, so you cannot re-distribute (e.g. copy via network) the app bundle extracted from the disk image independently in a notarized fashion, even though the notarization is recusively notarizing all contents. The stapling is key here. Since notarization happens rather quickly, I don’t see a point in bypassing it (except for academic curiosity) and personally prefer to err on the side of caution: Notarize both pieces so they can be used independently without caveats.
  • Josh also mentioned that fastlane can automate this process. Haven’t tried Fastlane for Mac apps in, what, 4 years or so? So theres the notarize action you might want to have a look at if you want Fastlane.
  • Brendan Duddridge recommended the app DMG Canvas to create disk images, code-sign the image file, and then notarize it in one go. Haven’t tried that tool, yet, but if you don’t own DropDMG, you might also want to trial this app.

Shift-Click in Emacs to Select – A Simple Change With a Complex History

Sometimes I actually do wonder why the Emacs defaults are whatever they are.

Someone somewhere decided that clicking with the left mouse button while holding shift should pop up an appearance menu where you can change font size and things like that.

The behavior I expected from my personal history of 25 years or so of using computers is to extend the selection up to the clicked point.

In text buffers, that means: select the text up to there; in a GUI file manager that means I expect to select files from the initial selection up to the clicked file. But not in Emacs, ha!

The change is pretty simple. Luckily, by now I know what functions are usually named in Emacs; it’s nothing with “selection” in it, it has to be “mark” and “set”, and “mouse” as a prefix most likely. And yes, mouse-set-mark exists and works!

So here’s the change I want:

(global-set-key (kbd "S-<down-mouse-1>") #'mouse-set-mark)
;; was: mouse-appearance-menu

Looking for functions that might change the selection, I also found that by default, C-M-<down-mouse-1> allows rectangular selection. Usually, that’s bound to option-dragging in macOS text editors that support this, so I remapped that as well since A-<down-mouse-1> is free:

(global-set-key (kbd "A-<down-mouse-1>") #'mouse-drag-region-rectangle)

But as I wrote at the very beginning, I do wonder why the appearance menu is the default. Is that a recent addition? Or does this precede my birth? Everything’s possible in Emacs.

The mouse.el copyright line says:

;; Copyright (C) 1993-1995, 1999-2022 Free Software Foundation, Inc.

So the addition of mouse input isn’t ancient, but it’s potentially old.

Looking at the code from emacs-mirror on GitHub, we can inspect when the binding was added.

The git blame output of the key binding reveals quite a lot!

  • 30 years ago, JimB added a binding of drag-mouse-1 to mouse-set-region in the vicinity of this line. So drag to select is there since the early days of mouse.el.
  • 14 years ago, then, Chong Yidong added mouse-appearance-menu to Emacs 23 and bound it to [S-down-mouse-1] in this single commit.

Chong Yidong’s commit also does not reveal any deletion of a previous binding.
The decision to bind shift-click to the appearance menu is from Jun 15, 2008. I guess that this binding was free, so they just rolled with it.
So shift-click might just as well have been unbound for all of the 16 years that dragging to select was a thing, it seems. (We’ll see that this isn’t true in a minute.)

But in 2008, shift-click to select was a “thing” for computer users for a decade at least. I’m pretty sure we used this in Windows 95. I never touched a Mac in the 1990’s, but I would imagine that Mac OS with the signature single button mice had this modifier, too.

By 2008, you could easily install Linux distros on home computers, and chances were you already had a fast internet connection to download distros with a graphical desktop environment. KDE 3 was around the corner, GNOME 2 was already 6 years old, and I am somewhat certain that shift-click to select worked as expected, too. At least I don’t remember any surprises there.

Yet the Emacs dev team shipped shift-click to pop up a selection menu to change visual settings. That’s curious.

This binding is weird in 2022, and I argua that it was weird in 2008, too.

It probably was and still is less weird for people using Emacs and their computer in general without mouse input: if it’s irrelevant to you, you don’t know about common conventions, and chances are even if you do know, you don’t care. You can find people who think mouse support in Emacs in 2022 is still unnecessary.

Thanks to the emacs-devel mailing list archive, we can check the discussion from back then to find out more. Chong Yidong opened the discussion thus:

Currently, shift-down-mouse-1 pops up a menu that lets the user choose a
different font to apply to the current frame.

This doesn’t seem like a very useful operation for a mouse binding.

I strongly agree! The supposed change, though, doesn’t make much sense to me, either :)

Now that the face-remapping code has been checked in, I’d suggest
changing this menu to a “buffer faces” menu.

Nobody in the thread pointed out that shift-click selection exists; it wasn’t discarded because it wasn’t even brought up. Maybe nobody really cared? Or did people actually like the binding?

In 2003, it just occurred to Kai Großjohann “that the S-down-mouse-1 font menu should be available in the menu bar somewhere”, which means Chong Yidong improved a binding that was at least 5 years old.

Even in 2001, this already was a thing, another mailing list thread indicates.

So there we are now, 21+ years later.

In 2017, there’s been a suggestion by Tak Kunihiro to remap this to mouse-save-then-kill (which is also a bit weird because it copies the selection automatically), but Eli Zaretskii didn’t like that because it means retraining of muscle memory;

All that just to provide some kind of “consistency”?

(Yes, exactly because of that! :))

There’s not been a lot of discussion on the mailing list with regard to that binding. To cut to the source of things, Richard M. Stallman committed on Aug 31, 1992 a change that unbound shift-clicking in favor of dragging.

It’s been RMS’s fault all along :)


To me, this is a curious piece of Emacs history that makes me wonder who the people on emacs-devel were back then, what the binding was based on (“The current binding of S-mouse-1 was not taken out of thin air, it was modeled on other applications as well, AFAIR”), and what their stance on GUI and mouse input was.

Is this new binding after RMS’s removal a silent rebellion against Windows and Mac and also Linux desktop environment conventions?

Or is it merely an example that some folk simply do not put user experience of normies at the tippety top of their priority lists?

We can only speculate. (Or read the mailing list until we get a feeling for the dramatis personae.)

I don’t want to blame anyone here besides git-blame’ing their code. UX is especially hard to make consistent if there’s no guiding design principle; we see this in FOSS software projects a lot. “Design by committee” is a well-known saying in our industry for a reason.

In my interpretation, this merely reveals priorities. Improve an existing shift-click binding that was limited in its function with a better function. Make things incrementally better through tinkering.

On a much more positive note: we can inspect the Emacs history to find out what was going on, who was involved, and what the reasoning was.

Even if I, in 2022, find the shift-click binding to be weird, for almost 3 decades now people seemed to have a different vision. That’s a humbling revelation. Without the history, I wouldn’t have been able to figure this out, and my gut reaction would have remained – something along the lines of “this must’ve been some neckbeard without a mouse looking for a free key binding to satisfy a feature request on paper without thinking this through, gah!”

Free Cmd-W in Magit to Close the Buffer

Magit binds M-w to magit-copy-buffer-revision. On my Mac, I use the left Command () key as the Meta key, though, and wired M-x, M-c, M-v to cut/copy/paste, leaving M-w aka +W to close the current buffer.

Magit defines this key binding in a lot of modes, though. The status mode has its own key map, the log mode has its own key map – so I did the only sensible thing and removed this from all maps I could find.

Update 2022-06-10: Magit author Jonas Bernoulli pointed out I’m doing everything wrong, there’s just 1 key-map, all the others derive from it. So the actually sensible thing to do is just this:

(unbind-key "M-w" magit-mode-map)

In my defense I would like to complain that this fact isn’t very obvious from the built-in Emacs help!!1

Old approach before Jonas showed the true way

Here’s the previous code to unbind M-w from all Magit mode key maps:

(let ((all-magit-mode-maps
       '(magit-status-mode-map
         magit-mode-map
         magit-log-mode-map
         magit-blob-mode-map
         magit-diff-mode-map
         magit-refs-mode-map
         magit-blame-mode-map
         magit-stash-mode-map
         magit-cherry-mode-map
         magit-reflog-mode-map
         magit-process-mode-map
         magit-section-mode-map
         magit-stashes-mode-map
         magit-repolist-mode-map
         magit-revision-mode-map
         magit-log-select-mode-map
         magit-todos-list-mode-map
         magit-merge-preview-mode-map
         magit-submodule-list-mode-map
         magit-blame-read-only-mode-map)))
    (dolist (mode-map all-magit-mode-maps)
      (unbind-key "M-w" mode-map)))

To get there, I filtered all bound variables for “magit -mode-map”. That produced a list of 20 results.

Thanks to Embark, I ran C-, S ((embark-collect)) to produce a more permanent buffer from the minibuffer’s resuls. There, I copied the variable names with a rectangular selection and concatenated the 20 lines into one via xah-reformat-lines that ships with my modal input of choice, Xah Fly Keys mode. (I could’ve probably also have used fill/unfill shortcuts, I just realize.)

Collected results in a dedicated buffer to edit

Wrap that in a quoted list and loop over it to call unbind-key for each map.

It’s likely that some of these key maps didn’t even bind M-w, but that’s not a problem, it’s just a no-op.


→ Blog Archive