Categories: embark

RSS - Atom - Subscribe via email

Insert a link to an Org Mode heading from an org-refile prompt

| emacs, embark, org

I often want to link to an Org heading from somewhere in my org-refile-targets, which includes my agenda files and other things. I don't want to think about where the heading is, I just want to link to it. I could use C-u C-c C-w (org-refile) to go to the heading, use C-c l (my shortcut for org-store-link), head back with org-mark-ring-goto, and then insert it with C-c C-l (org-insert-link).

Or I can use this code to make an Embark command that hooks into minibuffer prompts that include "goto" or "refile", so I can link to something with C-. i right from a refile or goto query.

(defun my-embark-org-insert-link-from-path (path)
  (interactive (list (car (org-refile-get-location))))
  (let* ((extra (if org-refile-use-outline-path "/" ""))
         (tbl (mapcar
               (lambda (x)
                 (if (and (not (member org-refile-use-outline-path
                                       '(file full-file-path title)))
                          (not (equal filename (file-truename (nth 1 x)))))
                     (cons (concat (car x) extra " ("
                                   (file-name-nondirectory (nth 1 x)) ")")
                           (cdr x))
                   (cons (concat (car x) extra) (cdr x))))
               org-refile-target-table))
         link)
    (insert (save-window-excursion
              (save-excursion
                (org-goto-marker-or-bmk
                 (elt
                  (org-refile--get-location path tbl)
                  3))
                (org-store-link nil))))))
(defvar-keymap my-org-path-map
  :doc "Shortcuts for working with Org paths from `org-refile'."
  "i" #'my-embark-org-insert-link-from-path)
(with-eval-after-load 'marginalia
  (add-to-list 'marginalia-prompt-categories '("Goto\\|Refile" . my-org-path)))
(with-eval-after-load 'embark
  (add-to-list 'embark-keymap-alist '(my-org-path . my-org-path-map)))
2024-10-24_10-34-21.png
Figure 1: Screenshot of Embark menu invoked with "C-.", showing the new "i" shortcut for inserting a link

There are more Embark shortcuts in my Embark configuration.

This is part of my Emacs configuration.
View org source for this post

Yay Emacs 1: EmacsConf 2023 report, SVG animation, Embark, Org Mode links

| embark, yay-emacs, emacs

For this livestream, I experimented with scheduling it for 8:00 AM EST instead of just starting it whenever I could squeeze in the time.1 People dropped by! And asked questions! And suggested interesting things! Wow. This could be fun.

I wrote a bunch of blog posts throughout the week and added lots of little videos to them. It was easy to walk through my recent posts and demonstrate things without worrying about (a) accidentally leaking personal information or (b) flubbing things on camera, since apparently my multitasking abilities are on the way down.2 It felt good to go through them and add some more commentary and highlights while knowing that all the details are there in case people want to do a deeper dive.

Here are the links:

I roughly edited the transcript from Deepgram and I uploaded it to YouTube, fixing some bugs in my Deepgram VTT conversion along the way. I think I like having proper transcripts even for ephemeral stuff like this, since it costs roughly USD 0.21 for the 43-minute video and I can probably figure out how to make editing even faster..

New projects are easier to keep working on when they have immediate personal benefits. It's easy for me to keep doing Emacs News every week because I have so much fun learning about the cool things people are doing with Emacs. I think it'll be easy for me to keep doing Yay Emacs livestreams because not only do I get to capture some workflows and ideas in videos, but other people might even tell me about interesting things that could save me time or open up new possibilities. Also, it's worth building up things I love.

I'm going to try scheduling another stream for next Sunday (Jan 21) at 7:30 AM EST. Maybe I can experiment with sharing my screen with the Surface Book or the W530 and then using that computer to stream. We'll see what that's like!

Footnotes:

1

Thanks to the unpredictability of life with a kiddo, scheduling things has been one of my life goals for a while! <laugh> When I created the event, the kiddo was still in her winter-break habit of sleeping in until 10 or 11, so I figured that I had a little time before I needed to call in for her virtual school at 8:45 AM. Of course, that week she decided to start setting her alarm for 7:59 AM so that she could wake up early and have watching time, and she actually started waking up around that time. So for Friday, I woke up earlier (well, the cat woke got me up even earlier) and packed a little breakfast she could have in the living room (since my computer's on a kitchen cabinet)… and that was the one day she snoozed her alarm clock and sleep in. I've scheduled the next stream for 7:30 AM… and she has announced that she wants to set her alarm for 7:30ish. Hmm.

2

I notice that it can be a little challenging for me to talk and do things at the same time. This is particularly obvious when I'm cubing (brain hiccup at the last step, gotta solve the whole Rubik's cube all over again). It's also why I prefer to record the audio for my presentations separately instead of winging it. =) It could be verbal interference, (very mild, totally expected) age-related cognitive decline (which is a topic I've been meaning to write up my notes on), or my squirrel brain could just have been pretty bad at this all along. Anyway, words or code, sometimes I just gotta pick one. Never mind my laptop's CPU not handling ffmpeg well, my brain's CPU gets high utilization too. That's good, though!

View org source for this post

Using Embark and qrencode to show a QR code for the Org Mode link at point

Posted: - Modified: | embark, emacs, org

[2024-01-12 Fri]: Added some code to display the QR code on the right side.

John Kitchin includes little QR codes in his videos. I thought that was a neat touch that makes it easier for people to jump to a link while they're watching. I'd like to make it easier to show QR codes too. The following code lets me show a QR code for the Org link at point. Since many of my links use custom Org link types that aren't that useful for people to scan, the code reuses the link resolution code from https://sachachua.com/dotemacs#web-link so that I can get the regular https: link.

(defun my-org-link-qr (url)
  "Display a QR code for URL in a buffer."
  (let ((buf (save-window-excursion (qrencode--encode-to-buffer (my-org-stored-link-as-url url)))))
    (display-buffer-in-side-window buf '((side . right)))))

(use-package qrencode
  :config
  (with-eval-after-load 'embark
    (define-key embark-org-link-map (kbd "q") #'my-org-link-qr)))
qr-code.svg
Figure 1: Screenshot of QR code for the link at point
View org source for this post
This is part of my Emacs configuration.

EmacsConf backstage: jumping to and working with talks using Embark

| embark, emacs, emacsconf

In the course of organizing and running EmacsConf, I often need to jump to or act on specific talks. I have a function that jumps to the talk heading so that I can look up additional information or add notes.

output-2023-09-10-14:04:30.gif
Figure 1: Jumping to a talk

emacsconf-go-to-talk: Jump to the talk heading matching SEARCH.
(defun emacsconf-go-to-talk (search)
  "Jump to the talk heading matching SEARCH."
  (interactive (list (emacsconf-complete-talk)))
  (find-file emacsconf-org-file)
  (widen)
  (cond
   ((plist-get search :slug)
    (goto-char (org-find-property "SLUG" (plist-get search :slug))))
   ((emacsconf-get-slug-from-string search)
    (goto-char (org-find-property "SLUG" (emacsconf-get-slug-from-string search))))
   (t
    (goto-char
     (catch 'found
       (org-map-entries
        (lambda ()
          (when (string-match search
                              (cons
                               (concat (org-entry-get (point) "SLUG") " - "
                                       (org-entry-get (point) "ITEM") " - "
                                       (org-entry-get (point) "NAME") " - "
                                       (org-entry-get (point) "EMAIL"))
                               (point)))
            (throw 'found (point))))
        "SLUG={.}")))))
  (org-reveal))

Most of the work is done in a completion function that makes it easy to specify a talk using the slug (talk ID), title, or speaker names.

emacsconf-complete-talk: Offer talks for completion.
(defun emacsconf-complete-talk (&optional info)
  "Offer talks for completion.
If INFO is specified, limit it to that list."
  (let ((choices
         (if (and (null info) emacsconf-complete-talk-cache)
             emacsconf-complete-talk-cache
           (mapcar (lambda (o)
                     (string-join
                      (delq nil
                            (mapcar (lambda (f) (plist-get o f))
                                    '(:slug :title :speakers :irc)))
                      " - "))
                   (or info (emacsconf-get-talk-info))))))
    (completing-read
     "Talk: " 
     (lambda (string predicate action)
       (if (eq action 'metadata)
           '(metadata (category . emacsconf))
         (complete-with-action action choices string predicate))))))

In addition to jumping to the Org heading for a talk, there are a bunch of other things I might want to do. Embark lets me add a bunch of shortcuts for working with a talk. I could open the caption file, edit the talk's wiki page, change a talk's property, e-mail the speaker, or more. Here's the Embark-related code from emacsconf.el:

Embark-related code
;;; Embark
(defun emacsconf-embark-finder ()
  "Identify when we're on a talk subtree."
  (when (and (derived-mode-p 'org-mode)
             (org-entry-get-with-inheritance "SLUG"))
    (cons 'emacsconf (org-entry-get-with-inheritance "SLUG"))))

(defun emacsconf-insert-talk-title (search)
  "Insert the talk title matching SEARCH."
  (interactive (list (emacsconf-complete-talk)))
  (insert (plist-get (emacsconf-search-talk-info search) :title)))

(with-eval-after-load 'embark
  (add-to-list 'embark-target-finders 'emacsconf-embark-finder)
  (defvar-keymap embark-emacsconf-actions
    :doc "Keymap for emacsconf-related things"
    "a" #'emacsconf-announce
    "c" #'emacsconf-find-captions-from-slug
    "d" #'emacsconf-find-caption-directives-from-slug
    "p" #'emacsconf-set-property-from-slug
    "w" #'emacsconf-edit-wiki-page
    "s" #'emacsconf-set-start-time-for-slug
    "W" #'emacsconf-browse-wiki-page
    "u" #'emacsconf-update-talk
    "t" #'emacsconf-insert-talk-title
    "m" #'emacsconf-mail-speaker-from-slug
    "n" #'emacsconf-notmuch-search-mail-from-entry
    "f" #'org-forward-heading-same-level
    "b" #'org-backward-heading-same-level
    "RET" #'emacsconf-go-to-talk)
  (add-to-list 'embark-keymap-alist '(emacsconf . embark-emacsconf-actions)))

;;; Status updates

For example, I sometimes need to open the wiki page for a talk in order to update the talk description.

emacsconf-edit-wiki-page: Open the wiki page for the talk matching SEARCH.
;;; Embark
(defun emacsconf-embark-finder ()
  "Identify when we're on a talk subtree."
  (when (and (derived-mode-p 'org-mode)
             (org-entry-get-with-inheritance "SLUG"))
    (cons 'emacsconf (org-entry-get-with-inheritance "SLUG"))))

Embark can also act on completion candidates, so I can call any of those actions from my C-c e t shortcut for emacsconf-go-to-talk. This is specified by the (metadata (category . emacsconf)) in emacsconf-complete-talk and the (add-to-list 'embark-keymap-alist '(emacsconf . embark-emacsconf-actions)) in my Embark configuration.

C-. is the embark-act shortcut in my configuration. When I need to remember what the shortcuts are, I can use C-h (embark-keymap-help) to list the keyboard shortcuts or select the command with completion.

output-2023-09-10-20:45:31.gif
Figure 2: Embark help for Emacsconf talks

The code above and related functions are in emacsconf.el or other files in the emacsconf-el repository.