89 lines
10 KiB
Plaintext
89 lines
10 KiB
Plaintext
<h2 id="org-mode-links">Org-mode links</h2>
|
||
|
||
<p>One of the many selling point of using Org-mode as a personal wiki (aka <em>digital garden</em>, see <a href="https://www.eigenbahn.com/2021/09/15/org-roam">previous post</a>) is its <a href="https://orgmode.org/manual/Hyperlinks.html"><em>(hyper)link</em> feature</a>.</p>
|
||
|
||
<p>As we’ve seen previously, we can define <em>internal</em> links between Org-file (and/or their outlines), but also <a href="https://orgmode.org/manual/External-Links.html"><em>external</em> links</a> targeting external files & sytems. Those can either point to stuff openable in Emacs itself (e.g. link to files, to a shell command…) or external applications if configured as such (e.g. web pages).</p>
|
||
|
||
<h2 id="url-abbrevs">URL abbrevs</h2>
|
||
|
||
<p>Conveniently, pasted URL are directly resolved as <code class="language-plaintext highlighter-rouge">http(s)</code> links.</p>
|
||
|
||
<p>As usually, we may have a bunch of links to a few websites, it can be convenient to be able to have those in a shorter form.</p>
|
||
|
||
<p>Conveniently, Org-mode support <a href="https://orgmode.org/manual/Link-Abbreviations.html">link abbreviations</a>.</p>
|
||
|
||
<p>For example, we may define our list of recurrent linked website.</p>
|
||
|
||
<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">org-link-abbrev-alist</span> <span class="o">'</span><span class="p">((</span><span class="s">"gh"</span> <span class="o">.</span> <span class="s">"https://github.com/"</span><span class="p">)</span>
|
||
<span class="p">(</span><span class="s">"gl"</span> <span class="o">.</span> <span class="s">"https://gitlab.com/"</span><span class="p">)</span>
|
||
<span class="p">(</span><span class="s">"hn"</span> <span class="o">.</span> <span class="s">"https://news.ycombinator.com/item?id="</span><span class="p">)</span>
|
||
<span class="p">(</span><span class="s">"lines"</span> <span class="o">.</span> <span class="s">"https://llllllll.co/t/"</span><span class="p">)</span>
|
||
<span class="c1">;; [...]</span>
|
||
<span class="p">(</span><span class="s">"thing"</span> <span class="o">.</span> <span class="s">"https://www.thingiverse.com/thing:"</span><span class="p">)))</span>
|
||
</code></pre></div></div>
|
||
|
||
<p>One would just have to type <code class="language-plaintext highlighter-rouge">[[gh:emacs-mirror/emacs]]</code> instead of <code class="language-plaintext highlighter-rouge">https://github.com/emacs-mirror/emacs</code> to insert a link to Emacs’ github mirror.</p>
|
||
|
||
<h2 id="even-faster--shorter-url-abbrevs">Even faster & shorter URL abbrevs</h2>
|
||
|
||
<p>The above solution is nice if we use <code class="language-plaintext highlighter-rouge">org-insert-link</code> to insert links.</p>
|
||
|
||
<p>But I personally find this command cumbersome. It prompts me 3 times (link type, actual link, description).</p>
|
||
|
||
<p>I want to go faster and just having to type <code class="language-plaintext highlighter-rouge">gh:emacs-mirror/emacs</code> to get a valid link.</p>
|
||
|
||
<p>One way to achieve this is to declare <a href="https://orgmode.org/manual/Adding-Hyperlink-Types.html">custom link types</a>.</p>
|
||
|
||
<p>The solution becomes:</p>
|
||
|
||
<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">require</span> <span class="ss">'dash</span><span class="p">)</span>
|
||
|
||
<span class="p">(</span><span class="k">setq</span> <span class="nv">my-org-link-abbrev-alist</span> <span class="o">'</span><span class="p">((</span><span class="s">"gh"</span> <span class="o">.</span> <span class="s">"https://github.com/"</span><span class="p">)</span>
|
||
<span class="c1">;; [...]</span>
|
||
<span class="p">(</span><span class="s">"thing"</span> <span class="o">.</span> <span class="s">"https://www.thingiverse.com/thing:"</span><span class="p">)))</span>
|
||
|
||
<span class="p">(</span><span class="nv">--each</span> <span class="nv">my-org-link-abbrev-alist</span>
|
||
<span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">link-prefix</span> <span class="p">(</span><span class="nb">car</span> <span class="nv">it</span><span class="p">))</span>
|
||
<span class="p">(</span><span class="nv">browse-fn</span> <span class="o">`</span><span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nv">e</span><span class="p">)</span>
|
||
<span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">org-link-abbrev-alist</span> <span class="nv">my-org-link-abbrev-alist</span><span class="p">))</span>
|
||
<span class="p">(</span><span class="nv">browse-url</span> <span class="p">(</span><span class="nv">org-link-expand-abbrev</span> <span class="p">(</span><span class="nv">concat</span> <span class="o">,</span><span class="nv">link-prefix</span> <span class="s">":"</span> <span class="nv">e</span><span class="p">)))))))</span>
|
||
<span class="p">(</span><span class="nv">org-link-set-parameters</span> <span class="nv">link-prefix</span> <span class="ss">:follow</span> <span class="nv">browse-fn</span><span class="p">)))</span>
|
||
</code></pre></div></div>
|
||
|
||
<p>Please note that we renamed <code class="language-plaintext highlighter-rouge">org-link-abbrev-alist</code> to <code class="language-plaintext highlighter-rouge">my-org-link-abbrev-alist</code> to prevent having duplicated prefix entries if we ever want to call <code class="language-plaintext highlighter-rouge">org-insert-link</code>.</p>
|
||
|
||
<h2 id="auto-shortening-abbreved-links">Auto-shortening abbrev’ed links</h2>
|
||
|
||
<p>Most of the time, I just copy/paste an URL into an Org buffer.</p>
|
||
|
||
<p>Wouldn’t it be convenient if it would automagically shorten it if it correspond to a known abbrev?</p>
|
||
|
||
<p>Thankfully, this is relatively trivial by <em>advising</em> <code class="language-plaintext highlighter-rouge">org-yank</code>.</p>
|
||
|
||
<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">require</span> <span class="ss">'s</span><span class="p">)</span>
|
||
|
||
<span class="p">(</span><span class="nb">defun</span> <span class="nv">my-org-link-apply-prefix</span> <span class="p">(</span><span class="nv">txt</span><span class="p">)</span>
|
||
<span class="s">"Rework link TXT, swapping prefix w/ shorted one if matches `my-org-link-abbrev-alist'."</span>
|
||
<span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">prfx</span> <span class="p">(</span><span class="nv">--some</span> <span class="p">(</span><span class="nb">and</span> <span class="p">(</span><span class="nv">s-starts-with?</span> <span class="p">(</span><span class="nb">cdr</span> <span class="nv">it</span><span class="p">)</span> <span class="nv">txt</span><span class="p">)</span> <span class="p">(</span><span class="nb">not</span> <span class="p">(</span><span class="nb">string=</span> <span class="p">(</span><span class="nb">cdr</span> <span class="nv">it</span><span class="p">)</span> <span class="nv">txt</span><span class="p">))</span> <span class="nv">it</span><span class="p">)</span> <span class="nv">my-org-link-abbrev-alist</span><span class="p">)))</span>
|
||
<span class="p">(</span><span class="k">if</span> <span class="nv">prfx</span>
|
||
<span class="p">(</span><span class="nv">s-replace</span> <span class="p">(</span><span class="nb">cdr</span> <span class="nv">prfx</span><span class="p">)</span> <span class="p">(</span><span class="nv">concat</span> <span class="p">(</span><span class="nb">car</span> <span class="nv">prfx</span><span class="p">)</span> <span class="s">":"</span><span class="p">)</span> <span class="nv">txt</span><span class="p">)</span>
|
||
<span class="nv">txt</span><span class="p">)))</span>
|
||
|
||
<span class="p">(</span><span class="nv">defadvice</span> <span class="nv">org-yank</span> <span class="p">(</span><span class="nv">around</span> <span class="nv">prf/org-yank-prefix-link</span> <span class="nv">activate</span><span class="p">)</span>
|
||
<span class="s">"Advice around `org-yank' that will auto-compact current entry in `kill-ring' if it matches `my-org-link-abbrev-alist'."</span>
|
||
<span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">kill</span> <span class="p">(</span><span class="nb">or</span> <span class="p">(</span><span class="nb">and</span> <span class="nv">kill-ring</span> <span class="p">(</span><span class="nv">current-kill</span> <span class="mi">0</span><span class="p">))</span> <span class="s">""</span><span class="p">))</span>
|
||
<span class="p">(</span><span class="nv">new-kill</span> <span class="p">(</span><span class="nv">my-org-link-apply-prefix</span> <span class="nv">kill</span><span class="p">)))</span>
|
||
<span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nv">s-blank?</span> <span class="nv">new-kill</span><span class="p">)</span>
|
||
<span class="p">(</span><span class="nv">kill-new</span> <span class="nv">new-kill</span> <span class="no">t</span><span class="p">))</span>
|
||
<span class="nv">ad-do-it</span><span class="p">))</span>
|
||
</code></pre></div></div>
|
||
|
||
<p>Please not that <code class="language-plaintext highlighter-rouge">my-org-link-apply-prefix</code> only covers “basic” prefix abbrevs. It doesn’t support formatted abbrevs (<code class="language-plaintext highlighter-rouge">%s</code>, <code class="language-plaintext highlighter-rouge">%h</code>) nor formatting using a custom function (<code class="language-plaintext highlighter-rouge">%(<CUSTOM-FN>)</code> for which we’d need the inverse function).</p>
|
||
|
||
<h2 id="conclusion">Conclusion</h2>
|
||
|
||
<p>As always, this shows how flexible Emacs is and how expressive Elisp can be (once you are used to its quirks).</p>
|
||
|
||
<p>We showcased a very basic example of custom links. One could really go crazy with those.</p>
|
||
|
||
<p>For more in-depth examples, check out <a href="https://kitchingroup.cheme.cmu.edu/blog/2016/11/04/New-link-features-in-org-9/">this article</a> from the <em>The Kitchin Research Group</em> blog. They wrote <a href="https://kitchingroup.cheme.cmu.edu/blog/category/orgmode/">a bunch of advanced articles about Org</a>, notably about <em>Babel</em> (Org’s JupyterLab equivalent).</p> |