Browse Source

New blog post introducing nroam

master
Nicolas Petton 7 months ago
parent
commit
533a6dc86d
No known key found for this signature in database GPG Key ID: E8BCD7866AFCF978
12 changed files with 403 additions and 122 deletions
  1. +81
    -0
      _posts/2021-03-11-introduction-to-nroam.markdown
  2. +1
    -1
      _site/css/cafe.css
  3. +236
    -0
      _site/emacs/org-mode/org-roam/2021/03/11/introduction-to-nroam.html
  4. +72
    -120
      _site/feed.xml
  5. BIN
      _site/img/nroam-backlinks.png
  6. BIN
      _site/img/org-roam-backlinks.png
  7. BIN
      _site/img/same-backlinks-in-nroam.png
  8. +12
    -0
      _site/index.html
  9. +1
    -1
      css/cafe.css
  10. BIN
      img/nroam-backlinks.png
  11. BIN
      img/org-roam-backlinks.png
  12. BIN
      img/same-backlinks-in-nroam.png

+ 81
- 0
_posts/2021-03-11-introduction-to-nroam.markdown View File

@ -0,0 +1,81 @@
---
layout: post
title: "Introducing nroam"
date: 2021-03-11 20:20:00 +0200
author: Nicolas Petton
categories: emacs org-mode org-roam
description: "An altenative way to display org-roam backlinks"
---
## Introduction & rationale
I've been absolutely loving [org-roam](https://www.orgroam.com/) since I started
experimenting with it. It's a brilliant implementation of the concept of
bidirectional linking for `org-mode`—replacing hierarchies of notes—, however
the way it displays backlinks never really clicked for me.
Because backlinks in `org-roam` are only displayed in a side buffer, they are
easy to miss. The side buffer will not always be open, and even if it is, the
information it contains is visually far away from the note you're looking at.
I was reluctant to use the daily notes as much as I would have in applications
such as [Roam Research](https://roamresearch.com) or
[Logseq](https://logseq.com), in fear of content getting buried in notes that I
would not come back to.
Another issue with the side buffer implementation of backlinks in `org-roam` is
that it does not display the entire outline subtree of a backlink. This is I
think an essential part of the bidirectional linking feature of Roam for
surfacing content from other notes.
## Enter nroam
[nroam](https://github.com/nicolaspetton/nroam) is a supplementary package for
`org-roam` that replaces its backlink side buffer. Instead, it displays
org-roam backlinks at the end of org-roam buffers. The user can also click a
button to see unlinked references, all in the original `org-mode` source buffer.
One of the ideas behind nroam is to remove the friction and effort it takes to
consult backlinks. By making them an integral part of the org buffer, backlinks
also become more powerful. You could for instance make a sparse tree—see
`org-sparse-tree`—to filter the contents of a note *and* its backlinks at the
same time.
Below is a screenshot of what nroam looks like.
![Backlinks in nroam](/img/nroam-backlinks.png)
For a comparison, here’s how backlinks are rendered in vanilla `org-roam`:
![Backlinks in org-roam](/img/org-roam-backlinks.png)
And here are the same backlinks displayed with `nroam`:
![Same backlinks in nroam](/img/same-backlinks-in-nroam.png)
As you can see, the purpose is not only to display references within the notes
buffer, but also to display the entire org-mode subtree for each reference,
giving much more context to the reader.
## Why extend org-roam?
Alternative Emacs packages exist, such as
[gkroam](https://github.com/Kinneyzhang/gkroam), which I could have simply used,
but I decided instead to build nroam as a companion package for org-roam as I
believe we should build upon and extend the existing ecosystem of org-roam
instead of competing with it. Org-roam has gained a lot of traction recently and
does much of the heavy lifting already, so there is no need to compete with it.
## Installing nroam
nroam is still quite young and is not yet available in MELPA. A recipe is
waiting for review, but in the meantime you will have to manually install the
Elisp files in your `.emacs.d` from the [git
repository](https://github.com/nicolaspetton/nroam).
Once installed, simply add the following hook and you should be ready to go.
```lisp
(add-hook 'org-mode-hook #'nroam-setup-maybe)
```

+ 1
- 1
_site/css/cafe.css View File

@ -40,7 +40,7 @@ h1, h2, h3, h4, .site-title {
.post-content {
font-size: 1.4em;
line-height: 1.3em;
line-height: 1.6em;
}
.post-content h2 {


+ 236
- 0
_site/emacs/org-mode/org-roam/2021/03/11/introduction-to-nroam.html View File

@ -0,0 +1,236 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Introducing nroam</title>
<meta name="description" content="Introduction &amp;amp; rationale">
<link rel="shortcut icon" type="image/png" href="/favicon.png"/>
<link href="https://fonts.googleapis.com/css?family=Fira+Mono|Gentium+Book+Basic|Lato" rel="stylesheet">
<link rel="stylesheet" href="/assets/main.css">
<link rel="stylesheet" href="/css/cafe.css">
<link rel="canonical" href="https://emacs.cafe/emacs/org-mode/org-roam/2021/03/11/introduction-to-nroam.html">
<link rel="alternate" type="application/rss+xml" title="Emacs café" href="/feed.xml">
</head>
<body>
<header class="site-header" role="banner">
<div class="wrapper">
<a class="site-title" href="/"><img src="/img/emacscafe.png"/>Emacs café</a>
<nav class="site-nav">
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger">
<span class="menu-icon">
<svg viewBox="0 0 18 15" width="18px" height="15px">
<path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
<path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
<path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
</svg>
</span>
</label>
<div class="trigger">
<a class="page-link" href="/about/">About Emacs café</a>
</div>
</nav>
</div>
</header>
<main class="page-content" aria-label="Content">
<div class="wrapper">
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
<header class="post-header">
<h1 class="post-title" itemprop="name headline">Introducing nroam</h1>
<p class="post-meta">
<time datetime="2021-03-11T19:20:00+01:00" itemprop="datePublished">
Mar 11, 2021
</time>
<span itemprop="author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">Nicolas Petton</span></span>
</p>
</header>
<div class="post-content" itemprop="articleBody">
<h2 id="introduction--rationale">Introduction &amp; rationale</h2>
<p>I’ve been absolutely loving <a href="https://www.orgroam.com/">org-roam</a> since I started
experimenting with it. It’s a brilliant implementation of the concept of
bidirectional linking for <code class="highlighter-rouge">org-mode</code>—replacing hierarchies of notes—, however
the way it displays backlinks never really clicked for me.</p>
<p>Because backlinks in <code class="highlighter-rouge">org-roam</code> are only displayed in a side buffer, they are
easy to miss. The side buffer will not always be open, and even if it is, the
information it contains is visually far away from the note you’re looking at.</p>
<p>I was reluctant to use the daily notes as much as I would have in applications
such as <a href="https://roamresearch.com">Roam Research</a> or
<a href="https://logseq.com">Logseq</a>, in fear of content getting buried in notes that I
would not come back to.</p>
<p>Another issue with the side buffer implementation of backlinks in <code class="highlighter-rouge">org-roam</code> is
that it does not display the entire outline subtree of a backlink. This is I
think an essential part of the bidirectional linking feature of Roam for
surfacing content from other notes.</p>
<h2 id="enter-nroam">Enter nroam</h2>
<p><a href="https://github.com/nicolaspetton/nroam">nroam</a> is a supplementary package for
<code class="highlighter-rouge">org-roam</code> that replaces its backlink side buffer. Instead, it displays
org-roam backlinks at the end of org-roam buffers. The user can also click a
button to see unlinked references, all in the original <code class="highlighter-rouge">org-mode</code> source buffer.</p>
<p>One of the ideas behind nroam is to remove the friction and effort it takes to
consult backlinks. By making them an integral part of the org buffer, backlinks
also become more powerful. You could for instance make a sparse tree—see
<code class="highlighter-rouge">org-sparse-tree</code>—to filter the contents of a note <em>and</em> its backlinks at the
same time.</p>
<p>Below is a screenshot of what nroam looks like.</p>
<p><img src="/img/nroam-backlinks.png" alt="Backlinks in nroam" /></p>
<p>For a comparison, here’s how backlinks are rendered in vanilla <code class="highlighter-rouge">org-roam</code>:</p>
<p><img src="/img/org-roam-backlinks.png" alt="Backlinks in org-roam" /></p>
<p>And here are the same backlinks displayed with <code class="highlighter-rouge">nroam</code>:</p>
<p><img src="/img/same-backlinks-in-nroam.png" alt="Same backlinks in nroam" /></p>
<p>As you can see, the purpose is not only to display references within the notes
buffer, but also to display the entire org-mode subtree for each reference,
giving much more context to the reader.</p>
<h2 id="why-extend-org-roam">Why extend org-roam?</h2>
<p>Alternative Emacs packages exist, such as
<a href="https://github.com/Kinneyzhang/gkroam">gkroam</a>, which I could have simply used,
but I decided instead to build nroam as a companion package for org-roam as I
believe we should build upon and extend the existing ecosystem of org-roam
instead of competing with it. Org-roam has gained a lot of traction recently and
does much of the heavy lifting already, so there is no need to compete with it.</p>
<h2 id="installing-nroam">Installing nroam</h2>
<p>nroam is still quite young and is not yet available in MELPA. A recipe is
waiting for review, but in the meantime you will have to manually install the
Elisp files in your <code class="highlighter-rouge">.emacs.d</code> from the <a href="https://github.com/nicolaspetton/nroam">git
repository</a>.</p>
<p>Once installed, simply add the following hook and you should be ready to go.</p>
<pre><code class="language-lisp">(add-hook 'org-mode-hook #'nroam-setup-maybe)
</code></pre>
</div>
<div id="disqus_thread"></div>
<script>
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'emacs-cafe';
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
</article>
</div>
</main>
<footer class="site-footer">
<div class="wrapper">
<h2 class="footer-heading">Emacs café</h2>
<div class="footer-col-wrapper">
<div class="footer-col footer-col-1">
<ul class="contact-list">
<li>
Emacs café
</li>
<li><a href="mailto:nicolas@petton.fr">nicolas@petton.fr</a></li>
</ul>
</div>
<div class="footer-col footer-col-2">
<ul class="social-media-list">
<li>
<a href="https://github.com/NicolasPetton"><span class="icon icon--github"><svg viewBox="0 0 16 16" width="16px" height="16px"><path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/></svg>
</span><span class="username">NicolasPetton</span></a>
</li>
<li>
<a href="https://twitter.com/NicolasPetton"><span class="icon icon--twitter"><svg viewBox="0 0 16 16" width="16px" height="16px"><path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/></svg>
</span><span class="username">NicolasPetton</span></a>
</li>
</ul>
</div>
<div class="footer-col footer-col-3">
<p>A blog about Emacs, mostly focused on JavaScript development, by Nicolas Petton.
</p>
</div>
</div>
</div>
</footer>
</body>
</html>

+ 72
- 120
_site/feed.xml View File

@ -1,5 +1,74 @@
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.5.1">Jekyll</generator><link href="https://emacs.cafe/feed.xml" rel="self" type="application/atom+xml" /><link href="https://emacs.cafe/" rel="alternate" type="text/html" /><updated>2018-11-28T15:27:55+01:00</updated><id>https://emacs.cafe/</id><title type="html">Emacs café</title><subtitle>A blog about Emacs, mostly focused on JavaScript development, by Nicolas Petton.
</subtitle><entry><title type="html">Indium 2.0 is out! 🎉</title><link href="https://emacs.cafe/emacs/javascript/indium/2018/11/28/indium-20-released.html" rel="alternate" type="text/html" title="Indium 2.0 is out! 🎉" /><published>2018-11-28T14:20:00+01:00</published><updated>2018-11-28T14:20:00+01:00</updated><id>https://emacs.cafe/emacs/javascript/indium/2018/11/28/indium-20-released</id><content type="html" xml:base="https://emacs.cafe/emacs/javascript/indium/2018/11/28/indium-20-released.html">&lt;p&gt;A while ago I detailed what was new with the upcoming major release of Indium.
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.5.1">Jekyll</generator><link href="https://emacs.cafe/feed.xml" rel="self" type="application/atom+xml" /><link href="https://emacs.cafe/" rel="alternate" type="text/html" /><updated>2021-03-11T21:26:38+01:00</updated><id>https://emacs.cafe/</id><title type="html">Emacs café</title><subtitle>A blog about Emacs, mostly focused on JavaScript development, by Nicolas Petton.
</subtitle><entry><title type="html">Introducing nroam</title><link href="https://emacs.cafe/emacs/org-mode/org-roam/2021/03/11/introduction-to-nroam.html" rel="alternate" type="text/html" title="Introducing nroam" /><published>2021-03-11T19:20:00+01:00</published><updated>2021-03-11T19:20:00+01:00</updated><id>https://emacs.cafe/emacs/org-mode/org-roam/2021/03/11/introduction-to-nroam</id><content type="html" xml:base="https://emacs.cafe/emacs/org-mode/org-roam/2021/03/11/introduction-to-nroam.html">&lt;h2 id=&quot;introduction--rationale&quot;&gt;Introduction &amp;amp; rationale&lt;/h2&gt;
&lt;p&gt;I’ve been absolutely loving &lt;a href=&quot;https://www.orgroam.com/&quot;&gt;org-roam&lt;/a&gt; since I started
experimenting with it. It’s a brilliant implementation of the concept of
bidirectional linking for &lt;code class=&quot;highlighter-rouge&quot;&gt;org-mode&lt;/code&gt;—replacing hierarchies of notes—, however
the way it displays backlinks never really clicked for me.&lt;/p&gt;
&lt;p&gt;Because backlinks in &lt;code class=&quot;highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; are only displayed in a side buffer, they are
easy to miss. The side buffer will not always be open, and even if it is, the
information it contains is visually far away from the note you’re looking at.&lt;/p&gt;
&lt;p&gt;I was reluctant to use the daily notes as much as I would have in applications
such as &lt;a href=&quot;https://roamresearch.com&quot;&gt;Roam Research&lt;/a&gt; or
&lt;a href=&quot;https://logseq.com&quot;&gt;Logseq&lt;/a&gt;, in fear of content getting buried in notes that I
would not come back to.&lt;/p&gt;
&lt;p&gt;Another issue with the side buffer implementation of backlinks in &lt;code class=&quot;highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; is
that it does not display the entire outline subtree of a backlink. This is I
think an essential part of the bidirectional linking feature of Roam for
surfacing content from other notes.&lt;/p&gt;
&lt;h2 id=&quot;enter-nroam&quot;&gt;Enter nroam&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nicolaspetton/nroam&quot;&gt;nroam&lt;/a&gt; is a supplementary package for
&lt;code class=&quot;highlighter-rouge&quot;&gt;org-roam&lt;/code&gt; that replaces its backlink side buffer. Instead, it displays
org-roam backlinks at the end of org-roam buffers. The user can also click a
button to see unlinked references, all in the original &lt;code class=&quot;highlighter-rouge&quot;&gt;org-mode&lt;/code&gt; source buffer.&lt;/p&gt;
&lt;p&gt;One of the ideas behind nroam is to remove the friction and effort it takes to
consult backlinks. By making them an integral part of the org buffer, backlinks
also become more powerful. You could for instance make a sparse tree—see
&lt;code class=&quot;highlighter-rouge&quot;&gt;org-sparse-tree&lt;/code&gt;—to filter the contents of a note &lt;em&gt;and&lt;/em&gt; its backlinks at the
same time.&lt;/p&gt;
&lt;p&gt;Below is a screenshot of what nroam looks like.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/nroam-backlinks.png&quot; alt=&quot;Backlinks in nroam&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For a comparison, here’s how backlinks are rendered in vanilla &lt;code class=&quot;highlighter-rouge&quot;&gt;org-roam&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/org-roam-backlinks.png&quot; alt=&quot;Backlinks in org-roam&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And here are the same backlinks displayed with &lt;code class=&quot;highlighter-rouge&quot;&gt;nroam&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/same-backlinks-in-nroam.png&quot; alt=&quot;Same backlinks in nroam&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the purpose is not only to display references within the notes
buffer, but also to display the entire org-mode subtree for each reference,
giving much more context to the reader.&lt;/p&gt;
&lt;h2 id=&quot;why-extend-org-roam&quot;&gt;Why extend org-roam?&lt;/h2&gt;
&lt;p&gt;Alternative Emacs packages exist, such as
&lt;a href=&quot;https://github.com/Kinneyzhang/gkroam&quot;&gt;gkroam&lt;/a&gt;, which I could have simply used,
but I decided instead to build nroam as a companion package for org-roam as I
believe we should build upon and extend the existing ecosystem of org-roam
instead of competing with it. Org-roam has gained a lot of traction recently and
does much of the heavy lifting already, so there is no need to compete with it.&lt;/p&gt;
&lt;h2 id=&quot;installing-nroam&quot;&gt;Installing nroam&lt;/h2&gt;
&lt;p&gt;nroam is still quite young and is not yet available in MELPA. A recipe is
waiting for review, but in the meantime you will have to manually install the
Elisp files in your &lt;code class=&quot;highlighter-rouge&quot;&gt;.emacs.d&lt;/code&gt; from the &lt;a href=&quot;https://github.com/nicolaspetton/nroam&quot;&gt;git
repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once installed, simply add the following hook and you should be ready to go.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-lisp&quot;&gt;(add-hook 'org-mode-hook #'nroam-setup-maybe)
&lt;/code&gt;&lt;/pre&gt;</content><author><name>Nicolas Petton</name></author><summary type="html">Introduction &amp;amp; rationale</summary></entry><entry><title type="html">Indium 2.0 is out! 🎉</title><link href="https://emacs.cafe/emacs/javascript/indium/2018/11/28/indium-20-released.html" rel="alternate" type="text/html" title="Indium 2.0 is out! 🎉" /><published>2018-11-28T14:20:00+01:00</published><updated>2018-11-28T14:20:00+01:00</updated><id>https://emacs.cafe/emacs/javascript/indium/2018/11/28/indium-20-released</id><content type="html" xml:base="https://emacs.cafe/emacs/javascript/indium/2018/11/28/indium-20-released.html">&lt;p&gt;A while ago I detailed what was new with the upcoming major release of Indium.
The wait is now over, Indium 2.0.0 is finally out, and you can install it from
&lt;a href=&quot;https://melpa.org&quot;&gt;MELPA&lt;/a&gt;.&lt;/p&gt;
@ -1163,121 +1232,4 @@ environment for Emacs – version 0.6 has just been released!&lt;/p&gt;
&lt;p&gt;Installation and update instructions can be found in
the &lt;a href=&quot;https://indium.readthedocs.io/en/latest/&quot;&gt;documentation&lt;/a&gt;. You can also check out the project on &lt;a href=&quot;https://github.com/NicolasPetton/Indium&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many thanks to &lt;a href=&quot;https://github.com/TatriX&quot;&gt;Tatrix&lt;/a&gt; for his contributions to this release!&lt;/p&gt;</content><author><name>Nicolas Petton</name></author><summary type="html">Indium – the JavaScript development environment for Emacs – version 0.6 has just been released!</summary></entry><entry><title type="html">How to find all unused functions in JS buffers</title><link href="https://emacs.cafe/emacs/javascript/2017/05/12/finding-unused-functions.html" rel="alternate" type="text/html" title="How to find all unused functions in JS buffers" /><published>2017-05-12T14:43:00+02:00</published><updated>2017-05-12T14:43:00+02:00</updated><id>https://emacs.cafe/emacs/javascript/2017/05/12/finding-unused-functions</id><content type="html" xml:base="https://emacs.cafe/emacs/javascript/2017/05/12/finding-unused-functions.html">&lt;p&gt;The real power of Emacs lies in its extensibility. To be able to quickly hack
some Elisp together to fix a specific problem right in your development
environment is something quite unique to Emacs, and it makes it stand apart from
other text editors.&lt;/p&gt;
&lt;p&gt;I’m working on a fairly large JavaScript code base for which maintenance can
sometimes be an issue.&lt;/p&gt;
&lt;p&gt;Yesterday I wanted to quickly find all function definitions in a JavaScript file
that were not referenced anymore in the project, so I decided to hack some
Elisp to do that.&lt;/p&gt;
&lt;h2 id=&quot;what-do-we-already-have&quot;&gt;What do we already have?&lt;/h2&gt;
&lt;p&gt;Let’s see what building blocks are already available.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nicolaspetton/xref-js2&quot;&gt;xref-js2&lt;/a&gt; makes it easy to find all
references to a specific function within a project,
and &lt;a href=&quot;https://github.com/mooz/js2-mode&quot;&gt;js2-mode&lt;/a&gt; exposes an AST that can be
visited.&lt;/p&gt;
&lt;p&gt;All in all, what I want to achieve shouldn’t be too hard to implement!&lt;/p&gt;
&lt;h2 id=&quot;first-steps&quot;&gt;First steps&lt;/h2&gt;
&lt;p&gt;I’m calling my small package &lt;code class=&quot;highlighter-rouge&quot;&gt;js2-unused&lt;/code&gt;, so all functions and variables will
have that prefix.&lt;/p&gt;
&lt;p&gt;We’ll need some packages along the way, so let’s require them:&lt;/p&gt;
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'xref-js2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'subr-x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;The first step is to find all function definitions within the current buffer.
&lt;code class=&quot;highlighter-rouge&quot;&gt;JS2-mode&lt;/code&gt; has a function &lt;code class=&quot;highlighter-rouge&quot;&gt;js2-visit-ast&lt;/code&gt; that makes it really easy to traverse
the entire AST tree.&lt;/p&gt;
&lt;p&gt;We can first define a variable that will hold all function definition names that
we find:&lt;/p&gt;
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defvar&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused-definitions&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Now let’s traverse the AST and find all function definitions. We want to find:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;all assignments that assign to a function;&lt;/li&gt;
&lt;li&gt;all function declarations that are named (skipping anonymous functions).&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused--find-definitions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Reset the value before visiting the AST&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused-definitions&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-visit-ast&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-mode-ast&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-unused-visitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused-visitor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;end-p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;&quot;Add NODE's name to `js2-unused-definitions` if it is a function.&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;end-p&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cond&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; assignment to a function&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-assign-node-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-function-node-p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-assign-node-right&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-node-string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-assign-node-left&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused-definitions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; function declaration (skipping anonymous ones)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-function-node-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;if-let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-function-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused-definitions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;finding-references-using-xref-js2&quot;&gt;Finding references using xref-js2&lt;/h2&gt;
&lt;p&gt;Now that we can find and store all function names in a list, let’s use
&lt;code class=&quot;highlighter-rouge&quot;&gt;xref-js2&lt;/code&gt; to filter the ones that are never referenced. If we find
unreferenced functions, we simply display a message listing them.&lt;/p&gt;
&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-elisp&quot; data-lang=&quot;elisp&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused-functions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Make sure that JS2 has finished parsing the buffer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-mode-wait-for-parse&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Walk the AST tree to find all function definitions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-unused--find-definitions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Use xref-js2 to filter the ones that are not referenced anywhere&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;unused&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seq-filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;xref-js2--find-references&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;js2-unused--unqualified-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;js2-unused-definitions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; If there are unreferenced function, display a message&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unused&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unused functions in %s: %s &quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file-name-nondirectory&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;mapconcat&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unused&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;No unused function found&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js2-unused--unqualified-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;&quot;Return the local name of NAME.
foo.bar.baz =&amp;gt; baz&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-match-data&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;string-match&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\.\\([^.]+\\)$&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;match-string&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That’s it! In ~30 lines we can now find unreferenced functions in any JS file.
Sure, the code is not perfect, far from it, but it was hacked together in 10
minutes and gets the job done.&lt;/p&gt;
&lt;p&gt;Quickly writing some lisp code to fix a specific problem is something I do very
often. Most of the time, it’s code I throw away as soon as the task is
completed, but from time to time it’s something generic enough to be reused
later, in which case I save it in my &lt;code class=&quot;highlighter-rouge&quot;&gt;emacs.d&lt;/code&gt;, or make a proper package out of
it.&lt;/p&gt;
&lt;p&gt;If you find this feature useful, you can grab it from
my
&lt;a href=&quot;https://gitlab.petton.fr/nico/emacs.d/blob/master/local-packages/js2-unused.el&quot;&gt;emacs.d&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicolas Petton</name></author><summary type="html">The real power of Emacs lies in its extensibility. To be able to quickly hack some Elisp together to fix a specific problem right in your development environment is something quite unique to Emacs, and it makes it stand apart from other text editors.</summary></entry></feed>
&lt;p&gt;Many thanks to &lt;a href=&quot;https://github.com/TatriX&quot;&gt;Tatrix&lt;/a&gt; for his contributions to this release!&lt;/p&gt;</content><author><name>Nicolas Petton</name></author><summary type="html">Indium – the JavaScript development environment for Emacs – version 0.6 has just been released!</summary></entry></feed>

BIN
_site/img/nroam-backlinks.png View File

Before After
Width: 955  |  Height: 760  |  Size: 91 KiB

BIN
_site/img/org-roam-backlinks.png View File

Before After
Width: 447  |  Height: 289  |  Size: 19 KiB

BIN
_site/img/same-backlinks-in-nroam.png View File

Before After
Width: 690  |  Height: 335  |  Size: 25 KiB

+ 12
- 0
_site/index.html View File

@ -76,6 +76,18 @@
<ul class="post-list">
<li>
<span class="post-meta">Mar 11, 2021</span>
<h2>
<a class="post-link" href="/emacs/org-mode/org-roam/2021/03/11/introduction-to-nroam.html">Introducing nroam</a>
</h2>
<div class="post-desc post-meta">An altenative way to display org-roam backlinks</div>
</li>
<li>
<span class="post-meta">Nov 28, 2018</span>


+ 1
- 1
css/cafe.css View File

@ -40,7 +40,7 @@ h1, h2, h3, h4, .site-title {
.post-content {
font-size: 1.4em;
line-height: 1.3em;
line-height: 1.6em;
}
.post-content h2 {


BIN
img/nroam-backlinks.png View File

Before After
Width: 955  |  Height: 760  |  Size: 91 KiB

BIN
img/org-roam-backlinks.png View File

Before After
Width: 447  |  Height: 289  |  Size: 19 KiB

BIN
img/same-backlinks-in-nroam.png View File

Before After
Width: 690  |  Height: 335  |  Size: 25 KiB

Loading…
Cancel
Save