<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/assets/rss.xsl" type="text/xsl"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
	<title>Sacha Chua - category - embark</title>
	<atom:link href="https://sachachua.com/blog/category/embark/feed/index.xml" rel="self" type="application/rss+xml" />
	<atom:link href="https://sachachua.com/blog/category/embark" rel="alternate" type="text/html" />
	<link>https://sachachua.com/blog/category/embark/feed/index.xml</link>
	<description>Emacs, sketches, and life</description>
	<lastBuildDate>Sat, 04 Apr 2026 02:23:03 GMT</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>11ty</generator>
  <item>
		<title>Insert a link to an Org Mode heading from an org-refile prompt</title>
		<link>https://sachachua.com/blog/2024/10/insert-a-link-to-an-org-mode-heading-from-an-org-refile-prompt/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Thu, 24 Oct 2024 14:17:47 GMT</pubDate>
    <category>emacs</category>
<category>embark</category>
<category>org</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2024/10/insert-a-link-to-an-org-mode-heading-from-an-org-refile-prompt/</guid>
		<description><![CDATA[<p>
I often want to link to an Org heading from somewhere in my <code>org-refile-targets</code>, 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 <code>C-u C-c C-w</code> (<code>org-refile</code>) to go to the heading, use <code>C-c l</code> (my shortcut for <code>org-store-link</code>), head back with <code>org-mark-ring-goto</code>, and then insert it with <code>C-c C-l</code> (<code>org-insert-link</code>).
</p>

<p>
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 <code>C-. i</code> right from a refile or goto query.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-embark-org-insert-link-from-path</span> (path)
  (<span class="org-keyword">interactive</span> (list (car (org-refile-get-location))))
  (<span class="org-keyword">let*</span> ((extra (<span class="org-keyword">if</span> org-refile-use-outline-path <span class="org-string">"/"</span> <span class="org-string">""</span>))
         (tbl (mapcar
               (<span class="org-keyword">lambda</span> (x)
                 (<span class="org-keyword">if</span> (<span class="org-keyword">and</span> (not (member org-refile-use-outline-path
                                       <span class="org-highlight-quoted-quote">'</span>(file full-file-path title)))
                          (not (equal filename (file-truename (nth 1 x)))))
                     (cons (concat (car x) extra <span class="org-string">" ("</span>
                                   (file-name-nondirectory (nth 1 x)) <span class="org-string">")"</span>)
                           (cdr x))
                   (cons (concat (car x) extra) (cdr x))))
               org-refile-target-table))
         link)
    (insert (<span class="org-keyword">save-window-excursion</span>
              (<span class="org-keyword">save-excursion</span>
                (org-goto-marker-or-bmk
                 (elt
                  (org-refile&#45;&#45;get-location path tbl)
                  3))
                (org-store-link nil))))))
(<span class="org-keyword">defvar-keymap</span> my-org-path-map
  <span class="org-builtin">:doc</span> <span class="org-doc">"Shortcuts for working with Org paths from `</span><span class="org-doc"><span class="org-constant">org-refile</span></span><span class="org-doc">'."</span>
  <span class="org-string">"i"</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-embark-org-insert-link-from-path</span>)
(<span class="org-keyword">with-eval-after-load</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">marginalia</span>
  (add-to-list <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">marginalia-prompt-categories</span> <span class="org-highlight-quoted-quote">'</span>(<span class="org-string">"Goto</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">|</span></span><span class="org-string">Refile"</span> . my-org-path)))
(<span class="org-keyword">with-eval-after-load</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">embark</span>
  (add-to-list <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">embark-keymap-alist</span> <span class="org-highlight-quoted-quote">'</span>(my-org-path . my-org-path-map)))
</pre>
</div>



<figure id="orgdabf43a">
<img src="https://sachachua.com/blog/2024/10/insert-a-link-to-an-org-mode-heading-from-an-org-refile-prompt/2024-10-24_10-34-21.png" alt="2024-10-24_10-34-21.png">

<figcaption><span class="figure-number">Figure 1: </span>Screenshot of Embark menu invoked with "C-.", showing the new "i" shortcut for inserting a link</figcaption>
</figure>

<p>
There are more Embark shortcuts in my <a href="https://sachachua.com/dotemacs#embark">Embark</a> configuration.
</p>

<div class="note">This is part of my <a href="https://sachachua.com/dotemacs#org-refile-insert-link">Emacs configuration.</a></div><div><a href="https://sachachua.com/blog/2024/10/insert-a-link-to-an-org-mode-heading-from-an-org-refile-prompt/index.org">View org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2024%2F10%2Finsert-a-link-to-an-org-mode-heading-from-an-org-refile-prompt%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Yay Emacs 1: EmacsConf 2023 report, SVG animation, Embark, Org Mode links</title>
		<link>https://sachachua.com/blog/2024/01/yay-emacs-2024-01-12-emacsconf-2023-report-svg-animation-embark-org-mode-links/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Sat, 13 Jan 2024 20:00:27 GMT</pubDate>
    <category>embark</category>
<category>yay-emacs</category>
<category>emacs</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2024/01/yay-emacs-2024-01-12-emacsconf-2023-report-svg-animation-embark-org-mode-links/</guid>
		<description><![CDATA[<p>
</p><div class="yt-video"><iframe width="456" height="315" title="YouTube video player" src="https://www.youtube-nocookie.com/embed/ahwbcfbp7sU?enablejsapi=1" frameborder="0" allowfullscreen="">nil</iframe><a href="https://www.youtube.com/watch?v=ahwbcfbp7sU">Watch on YouTube</a></div>
<p></p>

<p>
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.<sup><a id="fnr.scheduling" class="footref" href="https://sachachua.com/blog/feed/index.xml#fn.scheduling" role="doc-backlink">1</a></sup> People dropped by! And
asked questions! And suggested interesting things! Wow. This
could be fun.
</p>

<p>
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.<sup><a id="fnr.age" class="footref" href="https://sachachua.com/blog/feed/index.xml#fn.age" role="doc-backlink">2</a></sup> 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.
</p>

<p>
Here are the links:
</p>

<ul class="org-ul">
<li><a href="https://emacsconf.org/2023/report">EmacsConf 2023 report</a></li>
<li><a href="https://sachachua.com/blog/2024/01/animating-svg-topic-maps-with-inkscape-emacs-ffmpeg-and-reveal-js/">Animating SVG topic maps with Inkscape, Emacs, FFmpeg, and Reveal.js</a></li>
<li><a href="https://sachachua.com/blog/2024/01/using-embark-and-qrencode-to-show-a-qr-code-for-the-org-mode-link-at-point/">Using Embark and qrencode to show a QR code for the Org Mode link at point</a></li>
<li><a href="https://sachachua.com/blog/2024/01/using-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files/">Using an Emacs Lisp macro to define quick custom Org Mode links to project files; plus URLs and search</a></li>
</ul>

<p>
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..
</p>

<p>
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.
</p>

<p>
I'm going to try scheduling another stream for <a href="https://www.youtube.com/watch?v=Pc2kpqgg8pU">next Sunday
(Jan 21) at 7:30 AM EST</a>. 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!
</p>
<div id="footnotes">
<h3 class="footnotes">Footnotes: </h3>
<div id="text-footnotes">

<div class="footdef"><sup><a id="fn.scheduling" class="footnum" href="https://sachachua.com/blog/feed/index.xml#fnr.scheduling" role="doc-backlink">1</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
Thanks to the unpredictability of life with
a kiddo, scheduling things has been one of my life goals for
a while! &lt;laugh&gt; 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)&#x2026;
and that was the one day she snoozed her alarm clock and
sleep in. I've scheduled the next stream for 7:30 AM&#x2026; and
she has announced that she wants to set her alarm for
7:30ish. Hmm.
</p></div></div>

<div class="footdef"><sup><a id="fn.age" class="footnum" href="https://sachachua.com/blog/feed/index.xml#fnr.age" role="doc-backlink">2</a></sup> <div class="footpara" role="doc-footnote"><p class="footpara">
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!
</p></div></div>


</div>
</div><div><a href="https://sachachua.com/blog/2024/01/yay-emacs-2024-01-12-emacsconf-2023-report-svg-animation-embark-org-mode-links/index.org">View org source for this post</a></div>

<p>You can <a href="https://sachachua.com/blog/2024/01/yay-emacs-2024-01-12-emacsconf-2023-report-svg-animation-embark-org-mode-links/#comment">view 4 comments</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2024%2F01%2Fyay-emacs-2024-01-12-emacsconf-2023-report-svg-animation-embark-org-mode-links%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Using Embark and qrencode to show a QR code for the Org Mode link at point</title>
		<link>https://sachachua.com/blog/2024/01/using-embark-and-qrencode-to-show-a-qr-code-for-the-org-mode-link-at-point/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Wed, 10 Jan 2024 20:46:11 GMT</pubDate>
    <category>embark</category>
<category>emacs</category>
<category>org</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2024/01/using-embark-and-qrencode-to-show-a-qr-code-for-the-org-mode-link-at-point/</guid>
		<description><![CDATA[<div class="update" id="org46d2494">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2024-01-12 Fri]</span></span>: Added some code to display the QR code on the right side.
</p>

</div>

<p>
John Kitchin includes <a href="https://www.youtube.com/watch?v=rGGAr1AWkTc">little QR codes in his videos</a>. 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 <a href="https://sachachua.com/dotemacs#web-link">https://sachachua.com/dotemacs#web-link</a> so that I can get the regular
<code>https:</code> link.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-org-link-qr</span> (url)
  <span class="org-doc">"Display a QR code for URL in a buffer."</span>
  (<span class="org-keyword">let</span> ((buf (<span class="org-keyword">save-window-excursion</span> (qrencode&#45;&#45;encode-to-buffer (my-org-stored-link-as-url url)))))
    (display-buffer-in-side-window buf <span class="org-highlight-quoted-quote">'</span>((side . right)))))

(<span class="org-keyword">use-package</span> <span class="org-constant">qrencode</span>
  <span class="org-builtin">:config</span>
  (<span class="org-keyword">with-eval-after-load</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">embark</span>
    (define-key embark-org-link-map (kbd <span class="org-string">"q"</span>) <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-org-link-qr</span>)))
</pre>
</div>



<figure id="org919c5fb">
<img src="https://sachachua.com/blog/2024/01/using-embark-and-qrencode-to-show-a-qr-code-for-the-org-mode-link-at-point/qr-code.svg" alt="qr-code.svg" class="org-svg">

<figcaption><span class="figure-number">Figure 1: </span>Screenshot of QR code for the link at point</figcaption>
</figure>
<div><a href="https://sachachua.com/blog/2024/01/using-embark-and-qrencode-to-show-a-qr-code-for-the-org-mode-link-at-point/index.org">View org source for this post</a></div>
<div class="note">This is part of my <a href="https://sachachua.com/dotemacs#embark-qr">Emacs configuration.</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2024%2F01%2Fusing-embark-and-qrencode-to-show-a-qr-code-for-the-org-mode-link-at-point%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>Using an Emacs Lisp macro to define quick custom Org Mode links to project files; plus URLs and search</title>
		<link>https://sachachua.com/blog/2024/01/using-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Sun, 07 Jan 2024 13:07:09 GMT</pubDate>
    <category>org</category>
<category>emacs</category>
<category>coding</category>
<category>embark</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2024/01/using-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files/</guid>
		<description><![CDATA[<div class="update" id="orgb05ba05">
<ul class="org-ul">
<li><span class="timestamp-wrapper"><span class="timestamp">[2025-03-27 Thu]</span></span>: Use my-org-project- as the prefix to avoid collisions.</li>
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-09-19 Thu]</span></span>: Added function for replacing current link, bound to <code>C-. r</code> (<code>my-embark-replace-link-with-exported-url</code>)</li>
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-01-12 Fri] </span></span> Added embark action to copy the exported link URL.</li>
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-01-11 Thu] </span></span> Switched to using Github links since Codeberg's down.</li>
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-01-11 Thu] </span></span> Updated my-copy-link to just return the link if called from Emacs Lisp. Fix getting the properties.</li>
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-01-08 Mon] </span></span> Add tip from Omar about <code>embark-around-action-hooks</code></li>
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-01-08 Mon] </span></span> Simplify code by using <code>consult&#45;&#45;grep-position</code></li>
</ul>

</div>

<summary id="orgb856eb0">
<p>
Summary (882 words): Emacs macros make it easy to define sets of related functions for custom Org links. This makes it easier to link to projects and export or copy the links to the files in the web-based repos. You can also use that information to consult-ripgrep across lots of projects.
</p>
</summary>

<p>
I'd like to get better at writing notes while coding and at turning
those notes into blog posts and videos. I want to be able to link to
files in projects easily with the ability to complete, follow, and
export links. For example, <code>[[subed:subed.el]]</code> should become
<a href="https://github.com/sachac/subed/blob/main/subed/subed.el">subed.el</a>, which opens the file if I'm in Emacs and exports a
link if I'm publishing a post. I've been making custom link types
using <code>org-link-set-parameters</code>. I think it's time to make a macro
that defines that set of functions for me. Emacs Lisp macros are a
great way to write code to write code.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp" id="org995be3e">(<span class="org-keyword">defvar</span> <span class="org-variable-name">my-project-web-base-list</span> nil <span class="org-doc">"Local path . web repo URLs for easy linking."</span>)

(<span class="org-keyword">defmacro</span> <span class="org-function-name">my-org-project-link</span> (type file-path git-url)
  <span class="org-highlight-quoted-quote">`</span>(<span class="org-keyword">progn</span>
     (<span class="org-keyword">defun</span> ,(intern (format <span class="org-string">"my-org-project-%s-complete"</span> type)) ()
       ,(format <span class="org-string">"Complete a file from %s."</span> type)
       (concat ,type <span class="org-string">":"</span> (completing-read <span class="org-string">"File: "</span>
                                          (projectile-project-files ,file-path))))
     (<span class="org-keyword">defun</span> ,(intern (format <span class="org-string">"my-org-project-%s-follow"</span> type)) (link _)
       ,(format <span class="org-string">"Open a file from %s."</span> type)
       (find-file
        (expand-file-name
         link
         ,file-path)))
     (<span class="org-keyword">defun</span> ,(intern (format <span class="org-string">"my-org-project-%s-export"</span> type)) (link desc format _)
       <span class="org-doc">"Export link to file."</span>
       (<span class="org-keyword">setq</span> desc (<span class="org-keyword">or</span> desc link))
       (<span class="org-keyword">when</span> (<span class="org-keyword">and</span> ,git-url link)
         (<span class="org-keyword">setq</span> link (concat ,git-url (replace-regexp-in-string <span class="org-string">"^/"</span> <span class="org-string">""</span> link))))
       (<span class="org-keyword">pcase</span> format
         ((<span class="org-keyword">or</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">html</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">11ty</span>) (format <span class="org-string">"&lt;a href=\"%s\"&gt;%s&lt;/a&gt;"</span>
                                   link
                                   (<span class="org-keyword">or</span> desc link)))
         (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">md</span> (<span class="org-keyword">if</span> desc (format <span class="org-string">"[%s](%s)"</span> desc link)
                (format <span class="org-string">"&lt;%s&gt;"</span> link)))
         (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">latex</span> (format <span class="org-string">"\\href{%s}{%s}"</span> link desc))
         (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">texinfo</span> (format <span class="org-string">"@uref{%s,%s}"</span> link desc))
         (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">ascii</span> (format <span class="org-string">"%s (%s)"</span> desc link))
         (_ (format <span class="org-string">"%s (%s)"</span> desc link))))
     (org-link-set-parameters
        ,type
        <span class="org-builtin">:complete</span> (<span class="org-keyword">quote</span> ,(intern (format <span class="org-string">"my-org-project-%s-complete"</span> type)))
        <span class="org-builtin">:export</span> (<span class="org-keyword">quote</span> ,(intern (format <span class="org-string">"my-org-project-%s-export"</span> type)))
        <span class="org-builtin">:follow</span> (<span class="org-keyword">quote</span> ,(intern (format <span class="org-string">"my-org-project-%s-follow"</span> type))))
     (<span class="org-keyword">cl-pushnew</span> (cons (expand-file-name ,file-path) ,git-url)
                 my-project-web-base-list
                 <span class="org-builtin">:test</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">equal</span>)))
</pre>
</div>


<p>
Then I can define projects this way:
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">with-eval-after-load</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org</span>
(<span class="org-keyword">my-org-project-link</span> <span class="org-string">"subed"</span>
                     <span class="org-string">"~/proj/subed/subed/"</span>
                     <span class="org-string">"https://github.com/sachac/subed/blob/main/subed/"</span>
                     <span class="org-comment-delimiter">;; </span><span class="org-comment">"https://codeberg.org/sachac/subed/src/branch/main/subed/"</span>
                     )
(<span class="org-keyword">my-org-project-link</span> <span class="org-string">"emacsconf-el"</span>
                     <span class="org-string">"~/proj/emacsconf/lisp/"</span>
                     <span class="org-string">"https://git.emacsconf.org/emacsconf-el/tree/"</span>)
(<span class="org-keyword">my-org-project-link</span> <span class="org-string">"subed-record"</span>
                     <span class="org-string">"~/proj/subed-record/"</span>
                     <span class="org-string">"https://github.com/sachac/subed-record/blob/main/"</span>
                     <span class="org-comment-delimiter">;; </span><span class="org-comment">"https://codeberg.org/sachac/subed-record/src/branch/main/"</span>
                     )
(<span class="org-keyword">my-org-project-link</span> <span class="org-string">"compile-media"</span>
                     <span class="org-string">"~/proj/compile-media/"</span>
                     <span class="org-string">"https://github.com/sachac/compile-media/blob/main/"</span>
                     <span class="org-comment-delimiter">;; </span><span class="org-comment">"https://codeberg.org/sachac/compile-media/src/branch/main/"</span>
                     )
(<span class="org-keyword">my-org-project-link</span> <span class="org-string">"ox-11ty"</span>
                     <span class="org-string">"~/proj/ox-11ty/"</span>
                     <span class="org-string">"https://github.com/sachac/ox-11ty/blob/master/"</span>)
(<span class="org-keyword">my-org-project-link</span> <span class="org-string">"11ty"</span>
                     <span class="org-string">"~/proj/static-blog/"</span>
                     <span class="org-string">"https://github.com/sachac/eleventy-blog-setup/blob/master/"</span>))
</pre>
</div>


<p>
And I can complete them with the usual <code>C-c C-l</code> (<code>org-insert-link</code>) process:
</p>


<figure id="orge06f949">
<img src="https://sachachua.com/blog/2024/01/using-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files/completing-custom-links.gif" alt="completing-custom-links.gif">

<figcaption><span class="figure-number">Figure 1: </span>Completing a custom link with <code>org-insert-link</code></figcaption>
</figure>

<p>
Sketches are handled by <a href="https://sachachua.com/dotemacs#org-mode-sketch-links">my Org Mode sketch links</a>, but we can add them anyway.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">cl-pushnew</span> (cons (expand-file-name <span class="org-string">"~/sync/sketches/"</span>) <span class="org-string">"https://sketches.sachachua.com/filename/"</span>)
            my-project-web-base-list
            <span class="org-builtin">:test</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">equal</span>)
</pre>
</div>


<p>
I've been really liking being able to refer to various emacsconf-el
files by just selecting the link type and completing the filename, so
maybe it'll be easier to write about lots of other stuff if I extend
that to my other projects.
</p>
<div id="outline-container-web-link" class="outline-2">
<h3 id="web-link">Copy web link</h3>
<div class="outline-text-2" id="text-web-link">
<div class="update" id="org59d46eb">
<ul class="org-ul">
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-01-20 Sat]</span></span>: Fix Wayback link handling.</li>
<li><span class="timestamp-wrapper"><span class="timestamp">[2024-01-19 Fri]</span></span>: Add Wayback machine.</li>
</ul>

</div>

<p>
Keeping a list of projects and their web versions also makes it easier
for me to get the URL for something. I try to post as much as possible
on the Web so that it's easier for me to find things again and so that
other people can pick up ideas from my notes. Things are a bit
scattered: <a href="https://sachachua.com">my blog</a>, repositories on <a href="https://github.com/sachac/">Github</a> and <a href="https://codeberg.org/sachac/">Codeberg</a>, <a href="https://sketches.sachachua.com">my
sketches</a>&#x2026; I don't want to think about <i>where</i> the code has ended
up, I just want to grab the URL. If I'm going to put the link into an
Org Mode document, that's super easy. I just take advantage of the
things I've added to <code>org-store-link</code>. If I'm going to put it into an
e-mail or a toot or wherever else, I just want the bare URL.
</p>

<p>
I can think of two ways to approach this. One is a command that copies
just the URL by figuring it out from the buffer filename, which allows
me to special-case a bunch of things:
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-copy-link</span> (<span class="org-type">&amp;optional</span> filename skip-links)
  <span class="org-doc">"Return the URL of this file.</span>
<span class="org-doc">If FILENAME is non-nil, use that instead.</span>
<span class="org-doc">If SKIP-LINKS is non-nil, skip custom links.</span>
<span class="org-doc">If we're in a Dired buffer, use the file at point."</span>
  (<span class="org-keyword">interactive</span>)
  (<span class="org-keyword">setq</span> filename (<span class="org-keyword">or</span> filename
                     (<span class="org-keyword">if</span> (derived-mode-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">dired-mode</span>) (dired-get-filename))
                     (buffer-file-name)))
  (<span class="org-keyword">if-let*</span>
      ((project-re (concat <span class="org-string">"</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">"</span> (regexp-opt (mapcar <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">car</span> my-project-web-base-list)) <span class="org-string">"</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">"</span>
                           <span class="org-string">"</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">.*</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">"</span>))
       (url (<span class="org-keyword">cond</span>
             ((<span class="org-keyword">and</span> (derived-mode-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-mode</span>)
                   (eq (org-element-type (org-element-context)) <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">link</span>)
                   (not skip-links))
              (<span class="org-keyword">pcase</span> (org-element-property <span class="org-builtin">:type</span> (org-element-context))
                ((<span class="org-keyword">or</span> <span class="org-string">"https"</span> <span class="org-string">"http"</span>)
                 (org-element-property <span class="org-builtin">:raw-link</span> (org-element-context)))
                (<span class="org-string">"yt"</span>
                 (org-element-property <span class="org-builtin">:path</span> (org-element-context)))
                <span class="org-comment-delimiter">;; </span><span class="org-comment">if it's a custom link, visit it and get the link</span>
                (_
                 (<span class="org-keyword">save-window-excursion</span>
                   (org-open-at-point)
                   (my-copy-link nil t)))))
             <span class="org-comment-delimiter">;; </span><span class="org-comment">links to my config usually have a CUSTOM_ID property</span>
             ((string= (buffer-file-name) (expand-file-name <span class="org-string">"~/sync/emacs/Sacha.org"</span>))
              (concat <span class="org-string">"https://sachachua.com/dotemacs#"</span> (org-entry-get-with-inheritance <span class="org-string">"CUSTOM_ID"</span>)))
             <span class="org-comment-delimiter">;; </span><span class="org-comment">blog post drafts have permalinks</span>
             ((<span class="org-keyword">and</span> (derived-mode-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-mode</span>) (org-entry-get-with-inheritance <span class="org-string">"EXPORT_ELEVENTY_PERMALINK"</span>))
              (concat <span class="org-string">"https://sachachua.com"</span> (org-entry-get-with-inheritance <span class="org-string">"EXPORT_ELEVENTY_PERMALINK"</span>)))
             <span class="org-comment-delimiter">;; </span><span class="org-comment">some projects have web repos</span>
             ((string-match
               project-re filename)
              (concat (assoc-default (match-string 1 filename) my-project-web-base-list)
                      (url-hexify-string (match-string 2 filename)))))))
      (<span class="org-keyword">progn</span>
        (<span class="org-keyword">when</span> (called-interactively-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">any</span>)
          (kill-new url)
          (message <span class="org-string">"%s"</span> url))
        url)
    (<span class="org-warning">error</span> <span class="org-string">"Couldn't figure out URL."</span>)))
</pre>
</div>


<p>
Another approach is to hitch a ride on the Org Mode link storage and
export functions and just grab the URL from whatever link I've stored
with <code>org-store-link</code>, which I've bound to <code>C-c l</code>. I almost always
have an HTML version of the exported link. We can even use XML parsing
instead of regular expressions.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-org-link-as-url</span> (link)
  <span class="org-doc">"Return the final URL for LINK."</span>
  (<span class="org-keyword">cond</span>
   ((string-match <span class="org-string">"^/"</span> link)
    (concat my-blog-base-url (replace-regexp-in-string <span class="org-string">"^/"</span> <span class="org-string">""</span> link)))
   ((string-match <span class="org-string">"^https://"</span> link)
    link)
   (t
    (<span class="org-keyword">dom-attr</span>
     (dom-by-tag
      (<span class="org-keyword">with-temp-buffer</span>
        (insert (org-export-string-as link <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">html</span> t))
        (xml-parse-region (point-min) (point-max)))
      <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">a</span>)
     <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">href</span>))))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-org-stored-link-as-url</span> (<span class="org-type">&amp;optional</span> link insert)
  <span class="org-doc">"Copy the stored link as a plain URL.</span>
<span class="org-doc">If LINK is specified, use that instead."</span>
  (<span class="org-keyword">interactive</span> (list nil current-prefix-arg))
  (<span class="org-keyword">setq</span> link (<span class="org-keyword">or</span> link (caar org-stored-links)))
  (<span class="org-keyword">let</span> ((url (<span class="org-keyword">if</span> link
                 (my-org-link-as-url link)
               (<span class="org-warning">error</span> <span class="org-string">"No stored link"</span>))))
    (<span class="org-keyword">when</span> (called-interactively-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">any</span>)
      (<span class="org-keyword">if</span> url
          (<span class="org-keyword">if</span> insert (insert url) (kill-new url))
        (<span class="org-warning">error</span> <span class="org-string">"Could not find URL."</span>)))
    url))

(<span class="org-keyword">ert-deftest</span> <span class="org-function-name">my-org-stored-link-as-url</span> ()
  (<span class="org-keyword">should</span>
   (string= (my-org-stored-link-as-url <span class="org-string">"[[dotemacs:web-link]]"</span>)
            <span class="org-string">"https://sachachua.com/dotemacs#web-link"</span>))
  (<span class="org-keyword">should</span>
   (string= (my-org-stored-link-as-url <span class="org-string">"[[dotemacs:org-mode-sketch-links][my Org Mode sketch links]]"</span>)
            <span class="org-string">"https://sachachua.com/dotemacs#org-mode-sketch-links"</span>)))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-embark-org-copy-exported-url-as-wayback</span> (link <span class="org-type">&amp;rest</span> _)
  (<span class="org-keyword">interactive</span> <span class="org-string">"MLink: "</span>)
  (<span class="org-keyword">let</span> ((url  (my-embark-org-copy-exported-url link)))
    (<span class="org-keyword">when</span> (not (string-match (regexp-quote <span class="org-string">"^https://web.archive.org"</span>) url))
      (<span class="org-keyword">setq</span> url (concat <span class="org-string">"https://web.archive.org/web/"</span> (format-time-string <span class="org-string">"%Y%m%d%H%M%S/"</span>)
                        url)))
    (<span class="org-keyword">when</span> (called-interactively-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">any</span>)
      (kill-new url)
      (message <span class="org-string">"Copied %s"</span> url))
    url))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-embark-org-copy-exported-url</span> (link <span class="org-type">&amp;rest</span> _)
  (<span class="org-keyword">interactive</span> <span class="org-string">"MLink: \np"</span>)
  (<span class="org-keyword">let</span> ((url (my-org-link-as-url link)))
    (<span class="org-keyword">when</span> (<span class="org-keyword">and</span> (derived-mode-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-mode</span>)
               (org-entry-get-with-inheritance <span class="org-string">"EXPORT_ELEVENTY_PERMALINK"</span>)
               (string-match <span class="org-string">"^/"</span> url))
      <span class="org-comment-delimiter">;; </span><span class="org-comment">local file links are copied to blog directories</span>
      (<span class="org-keyword">setq</span> url (concat <span class="org-string">"https://sachachua.com"</span>
                        (org-entry-get-with-inheritance <span class="org-string">"EXPORT_ELEVENTY_PERMALINK"</span>)
                        (replace-regexp-in-string
                         <span class="org-string">"[\\?&amp;].*"</span>
                         <span class="org-string">""</span>
                         (file-name-nondirectory link)))))
    (<span class="org-keyword">when</span> (called-interactively-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">any</span>)
      (kill-new url)
      (message <span class="org-string">"Copied %s"</span> url))
    url))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-embark-replace-link-with-exported-url</span> (link <span class="org-type">&amp;rest</span> _)
  (<span class="org-keyword">interactive</span> (list (org-element-property <span class="org-builtin">:raw-link</span> (org-element-context))))
  (my-insert-or-replace-link (my-org-link-as-url link)))

(<span class="org-keyword">with-eval-after-load</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">embark-org</span>
  (mapc (<span class="org-keyword">lambda</span> (map)
          (keymap-set map <span class="org-string">"u"</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-embark-org-copy-exported-url</span>)
          (keymap-set map <span class="org-string">"U"</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-embark-org-copy-exported-url-as-wayback</span>)
          (keymap-set map <span class="org-string">"r e"</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-embark-replace-link-with-exported-url</span>))
        (list embark-url-map embark-org-link-map embark-org-link-copy-map)))
</pre>
</div>


<p>
We'll see which one I end up using. I think both approaches might come in handy.
</p>
</div>
</div>
<div id="outline-container-org7beda7c" class="outline-2">
<h3 id="org7beda7c">Quickly search my code</h3>
<div class="outline-text-2" id="text-org7beda7c">
<p>
Since <code>my-project-web-base-list</code> is a list of projects I often think
about or write about, I can also make something that searches through
them. That way, I don't have to care about where my code is.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-consult-ripgrep-code</span> ()
  (<span class="org-keyword">interactive</span>)
  (consult-ripgrep (mapcar <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">car</span> my-project-web-base-list)))
</pre>
</div>


<p>
I can add <code>.rgignore</code> files in directories to tell ripgrep to ignore
things like <code>node_modules</code> or <code>*.json</code>.
</p>

<p>
I also want to search my Emacs configuration at the same time,
although links to my config are handled by <a href="https://sachachua.com/dotemacs#links-to-my-config">my dotemacs link type</a> so
I'll leave the URL as nil. This is also the way I can handle other
unpublished directories.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">cl-pushnew</span> (cons (expand-file-name <span class="org-string">"~/sync/emacs/Sacha.org"</span>) nil)
            my-project-web-base-list
            <span class="org-builtin">:test</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">equal</span>)
(<span class="org-keyword">cl-pushnew</span> (cons (expand-file-name <span class="org-string">"~/proj/static-blog/_includes"</span>) nil)
            my-project-web-base-list
            <span class="org-builtin">:test</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">equal</span>)
(<span class="org-keyword">cl-pushnew</span> (cons (expand-file-name <span class="org-string">"~/bin"</span>) nil)
            my-project-web-base-list
            <span class="org-builtin">:test</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">equal</span>)
</pre>
</div>


<p>
Actually, let's throw my blog posts and Org files in there as well,
since I often have code snippets. If it gets to be too much, I can
always have different commands search different things.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">cl-pushnew</span> (cons (expand-file-name <span class="org-string">"~/proj/static-blog/blog/"</span>) <span class="org-string">"https://sachachua.com/blog/"</span>)
            my-project-web-base-list
            <span class="org-builtin">:test</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">equal</span>)
(<span class="org-keyword">cl-pushnew</span> (cons (expand-file-name <span class="org-string">"~/sync/orgzly"</span>) nil)
            my-project-web-base-list
            <span class="org-builtin">:test</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">equal</span>)
</pre>
</div>



<figure id="orga881673">
<img src="https://sachachua.com/blog/2024/01/using-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files/ripgrep-code.gif" alt="ripgrep-code.gif">

<figcaption><span class="figure-number">Figure 2: </span>Using my-consult-ripgrep-code</figcaption>
</figure>

<p>
I don't have anything bound to <code>M-s c</code> (code) yet, so let's try that.
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(keymap-global-set <span class="org-string">"M-s c"</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-consult-ripgrep-code</span>)
</pre>
</div>


<p>
At some point, it might be fun to get Embark set up so that I can grab
a link to something right from the consult-ripgrep interface. In the
meantime, I can always jump to it and get the link.
</p>
</div>
<div id="outline-container-org81e85f0" class="outline-3">
<h4 id="org81e85f0">Tip from Omar: embark-around-action-hooks</h4>
<div class="outline-text-3" id="text-org81e85f0">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2024-01-07 Sun] </span></span> I modified oantolin's suggestion from the comments to work with <code>consult-ripgrep</code>, since <code>consult-ripgrep</code> gives me <code>consult-grep</code> targets instead of <code>consult-location</code>:
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">cl-defun</span> <span class="org-function-name">embark-consult&#45;&#45;at-location</span> (<span class="org-type">&amp;rest</span> args <span class="org-type">&amp;key</span> target type run <span class="org-type">&amp;allow-other-keys</span>)
  <span class="org-doc">"RUN action at the target location."</span>
  (<span class="org-keyword">save-window-excursion</span>
    (<span class="org-keyword">save-excursion</span>
      (<span class="org-keyword">save-restriction</span>
        (<span class="org-keyword">pcase</span> type
          (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">consult-location</span> (consult&#45;&#45;jump (consult&#45;&#45;get-location target)))
          (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-heading</span> (org-goto-marker-or-bmk (get-text-property 0 <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-marker</span> target)))
          (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">consult-grep</span> (consult&#45;&#45;jump (consult&#45;&#45;grep-position target)))
          (<span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">file</span> (find-file target)))
        (apply run args)))))

(<span class="org-keyword">cl-pushnew</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">embark-consult&#45;&#45;at-location</span> (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-store-link</span> embark-around-action-hooks))
</pre>
</div>


<p>
I think I can use it with <code>M-s c</code> to search for the code, then <code>C-.
C-c l</code> on the matching line, where <code>C-c l</code> is my regular keybinding
for storing links. Thanks, Omar!
</p>

<p>
In general, I don't want to have to think about where something is on
my laptop or where it's published on the Web, I just want to write
about it. One step closer, yay Emacs!</p>
</div>
</div>
</div>

<div class="note">This is part of my <a href="https://sachachua.com/dotemacs#git-projects">Emacs configuration.</a></div><div><a href="https://sachachua.com/blog/2024/01/using-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files/index.org">View org source for this post</a></div>

<p>You can <a href="https://sachachua.com/blog/2024/01/using-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files/#comment">view 6 comments</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2024%2F01%2Fusing-an-emacs-lisp-macro-to-define-quick-custom-org-mode-links-to-project-files%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item><item>
		<title>EmacsConf backstage: jumping to and working with talks using Embark</title>
		<link>https://sachachua.com/blog/2023/09/emacsconf-backstage-jumping-to-and-working-with-talks-using-embark/</link>
		<dc:creator><![CDATA[Sacha Chua]]></dc:creator>
		<pubDate>Mon, 11 Sep 2023 01:11:21 GMT</pubDate>
    <category>embark</category>
<category>emacs</category>
<category>emacsconf</category>
		<guid isPermaLink="false">https://sachachua.com/blog/2023/09/emacsconf-backstage-jumping-to-and-working-with-talks-using-embark/</guid>
		<description><![CDATA[<p>
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.
</p>


<figure id="org1859e8f">
<img src="https://sachachua.com/blog/2023/09/emacsconf-backstage-jumping-to-and-working-with-talks-using-embark/output-2023-09-10-14:04:30.gif" alt="output-2023-09-10-14:04:30.gif">

<figcaption><span class="figure-number">Figure 1: </span>Jumping to a talk</figcaption>
</figure>

<p>
 </p><details><summary>emacsconf-go-to-talk: Jump to the talk heading matching SEARCH.</summary><div class="org-src-container"><pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">emacsconf-go-to-talk</span> (search)
  <span class="org-doc">"Jump to the talk heading matching SEARCH."</span>
  (<span class="org-keyword">interactive</span> (list (emacsconf-complete-talk)))
  (find-file emacsconf-org-file)
  (widen)
  (<span class="org-keyword">cond</span>
   ((plist-get search <span class="org-builtin">:slug</span>)
    (goto-char (org-find-property <span class="org-string">"SLUG"</span> (plist-get search <span class="org-builtin">:slug</span>))))
   ((emacsconf-get-slug-from-string search)
    (goto-char (org-find-property <span class="org-string">"SLUG"</span> (emacsconf-get-slug-from-string search))))
   (t
    (goto-char
     (<span class="org-keyword">catch</span> <span class="org-highlight-quoted-quote">'</span><span class="org-constant">found</span>
       (org-map-entries
        (<span class="org-keyword">lambda</span> ()
          (<span class="org-keyword">when</span> (string-match search
                              (cons
                               (concat (org-entry-get (point) <span class="org-string">"SLUG"</span>) <span class="org-string">" - "</span>
                                       (org-entry-get (point) <span class="org-string">"ITEM"</span>) <span class="org-string">" - "</span>
                                       (org-entry-get (point) <span class="org-string">"NAME"</span>) <span class="org-string">" - "</span>
                                       (org-entry-get (point) <span class="org-string">"EMAIL"</span>))
                               (point)))
            (<span class="org-keyword">throw</span> <span class="org-highlight-quoted-quote">'</span><span class="org-constant">found</span> (point))))
        <span class="org-string">"SLUG={.}"</span>)))))
  (org-reveal))
</pre></div></details>
<p></p>

<p>
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.
</p>

<p>
 </p><details><summary>emacsconf-complete-talk: Offer talks for completion.</summary><div class="org-src-container"><pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">emacsconf-complete-talk</span> (<span class="org-type">&amp;optional</span> info)
  <span class="org-doc">"Offer talks for completion.</span>
<span class="org-doc">If INFO is specified, limit it to that list."</span>
  (<span class="org-keyword">let</span> ((choices
         (<span class="org-keyword">if</span> (<span class="org-keyword">and</span> (null info) emacsconf-complete-talk-cache)
             emacsconf-complete-talk-cache
           (mapcar (<span class="org-keyword">lambda</span> (o)
                     (string-join
                      (delq nil
                            (mapcar (<span class="org-keyword">lambda</span> (f) (plist-get o f))
                                    <span class="org-highlight-quoted-quote">'</span>(<span class="org-builtin">:slug</span> <span class="org-builtin">:title</span> <span class="org-builtin">:speakers</span> <span class="org-builtin">:irc</span>)))
                      <span class="org-string">" - "</span>))
                   (<span class="org-keyword">or</span> info (emacsconf-get-talk-info))))))
    (completing-read
     <span class="org-string">"Talk: "</span> 
     (<span class="org-keyword">lambda</span> (string predicate action)
       (<span class="org-keyword">if</span> (eq action <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">metadata</span>)
           <span class="org-highlight-quoted-quote">'</span>(metadata (category . emacsconf))
         (complete-with-action action choices string predicate))))))
</pre></div></details>
<p></p>

<p>
In addition to jumping to the Org heading for a talk, there are a
bunch of other things I might want to do. <a target="_blank" href="http://melpa.org/#/embark">Embark</a> 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 <a href="https://git.emacsconf.org/emacsconf-el/tree/emacsconf.el">emacsconf.el</a>:
</p>

<p>
</p><details><summary>Embark-related code</summary><div class="org-src-container">
<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;;; </span><span class="org-comment">Embark</span>
(<span class="org-keyword">defun</span> <span class="org-function-name">emacsconf-embark-finder</span> ()
  <span class="org-doc">"Identify when we're on a talk subtree."</span>
  (<span class="org-keyword">when</span> (<span class="org-keyword">and</span> (derived-mode-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-mode</span>)
             (org-entry-get-with-inheritance <span class="org-string">"SLUG"</span>))
    (cons <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">emacsconf</span> (org-entry-get-with-inheritance <span class="org-string">"SLUG"</span>))))

(<span class="org-keyword">defun</span> <span class="org-function-name">emacsconf-insert-talk-title</span> (search)
  <span class="org-doc">"Insert the talk title matching SEARCH."</span>
  (<span class="org-keyword">interactive</span> (list (emacsconf-complete-talk)))
  (insert (plist-get (emacsconf-search-talk-info search) <span class="org-builtin">:title</span>)))

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

<span class="org-comment-delimiter">;;; </span><span class="org-comment">Status updates</span>
</pre>
</div></details>

<p></p>

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

<p>
 </p><details><summary>emacsconf-edit-wiki-page: Open the wiki page for the talk matching SEARCH.</summary><div class="org-src-container"><pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;;; </span><span class="org-comment">Embark</span>
(<span class="org-keyword">defun</span> <span class="org-function-name">emacsconf-embark-finder</span> ()
  <span class="org-doc">"Identify when we're on a talk subtree."</span>
  (<span class="org-keyword">when</span> (<span class="org-keyword">and</span> (derived-mode-p <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">org-mode</span>)
             (org-entry-get-with-inheritance <span class="org-string">"SLUG"</span>))
    (cons <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">emacsconf</span> (org-entry-get-with-inheritance <span class="org-string">"SLUG"</span>))))
</pre></div></details>
<p></p>

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

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


<figure id="orgc77ed50">
<img src="https://sachachua.com/blog/2023/09/emacsconf-backstage-jumping-to-and-working-with-talks-using-embark/output-2023-09-10-20:45:31.gif" alt="output-2023-09-10-20:45:31.gif">

<figcaption><span class="figure-number">Figure 2: </span>Embark help for Emacsconf talks</figcaption>
</figure>

<p>
The code above and related functions are in <a href="https://git.emacsconf.org/emacsconf-el/tree/emacsconf.el">emacsconf.el</a> or other files in the <a href="https://git.emacsconf.org/emacsconf-el/tree/">emacsconf-el</a> repository.
</p>

<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2023%2F09%2Femacsconf-backstage-jumping-to-and-working-with-talks-using-embark%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></description>
		</item>
	</channel>
</rss>