<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on
A Scripter's Notes</title><link>https://scripter.co/posts/</link><description>Recent content in Posts
on A Scripter's Notes</description><language>en-us</language><managingEditor>kaushal.modi@gmail.com (Kaushal Modi)</managingEditor><webMaster>kaushal.modi@gmail.com (Kaushal Modi)</webMaster><lastBuildDate>Wed, 22 Apr 2026 08:24:58 -0400</lastBuildDate><generator>Hugo -- gohugo.io</generator><docs>https://validator.w3.org/feed/docs/rss2.html</docs><atom:link href="https://scripter.co/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Version controlling Jenkins config</title><link>https://scripter.co/version-controlling-jenkins-config/</link><description>&lt;blockquote>Jenkins is an amazing free and open source continuous integration and
deployment software. But its primary means of configuration is a web
UI, and recently that cost me a lot of debug time. That set me down
the path of figuring out a way to version control the Jenkins config
(the &lt;code>$JENKINS_HOME&lt;/code> directory).&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#what-bit-me">What bit me&lt;/a>&lt;/li>
&lt;li>&lt;a href="#dot-gitignore-for-jenkins-config">&lt;code>.gitignore&lt;/code> for Jenkins config&lt;/a>&lt;/li>
&lt;li>&lt;a href="#jenkins-plugin-manager">Jenkins Plugin Manager&lt;/a>&lt;/li>
&lt;li>&lt;a href="#full-solution">Full solution&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>&lt;a href="https://www.jenkins.io/">Jenkins&lt;/a> is a wonderful piece of software and I use it with my
Bitbucket git repos for &lt;a href="https://en.wikipedia.org/wiki/CI/CD">CI/CD&lt;/a>.&lt;/p>
&lt;p>Jenkins uses a web UI for its configuration. I dislike that because
it&amp;rsquo;s difficult to document the configuration process without
screenshots, and if I need to create a new server, it&amp;rsquo;s a manual
process of clicking through tabs and filling in the text boxes. I
didn&amp;rsquo;t mind this enough to do anything about it .. that is until I
finally got bit by it.&lt;/p>
&lt;h2 id="what-bit-me">What bit me&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#what-bit-me">#&lt;/a>&lt;/h2>
&lt;p>Without going into too much detail, that issue was multi-fold:&lt;/p>
&lt;ol>
&lt;li>I had unknowingly messed up the &lt;em>Project-based Matrix Authorization
Strategy&lt;/em> such that other users in my team were not able to view
the Jenkins jobs.&lt;/li>
&lt;li>I had also updated the Jenkins server that introduced a bug
(&lt;a href="https://issues.jenkins.io/browse/JENKINS-68748">JENKINS-68748&lt;/a>) where the &lt;em>Test LDAP Settings&lt;/em> failed with an
error, but the LDAP authentication actually worked!&lt;/li>
&lt;li>I had also updated all the plugins after updating Jenkins. So if I
rolled back the Jenkins versions, most of the plugins would fail
because of incompatibility with the older Jenkins version. I had
updated Jenkins after months!&lt;/li>
&lt;/ol>
&lt;p>That&amp;rsquo;s when I wished that my whole Jenkins was
version-controlled. That would have allowed me to roll back to the
last working &amp;ldquo;Jenkins image&amp;rdquo; with the Jenkin version, plugins'
versions and my Jenkins config all in sync.&lt;/p>
&lt;p>I had delayed doing this because my &lt;code>$JENKINS_HOME&lt;/code> was more than 1GB
in size and I didn&amp;rsquo;t have time or motivation to figure out what stuff
I should commit and what I should ignore .. But no more &amp;mdash; The time
had finally come.&lt;/p>
&lt;h2 id="dot-gitignore-for-jenkins-config">&lt;code>.gitignore&lt;/code> for Jenkins config&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#dot-gitignore-for-jenkins-config">#&lt;/a>&lt;/h2>
&lt;p>So I did what any good engineer would do .. start looking for a
solution online. I found &lt;a href="https://stackoverflow.com/a/4695615">this StackOverflow answer&lt;/a> for &lt;em>Is there a way
to keep Hudson / Jenkins configuration files in source control?&lt;/em>.&lt;/p>
&lt;p>That answer shares a &lt;code>.gitignore&lt;/code> that ignores files not necessary for
configuring a Jenkins server &amp;mdash; Example: job builds, workspace, log
files, etc. But it didn&amp;rsquo;t work out of the box because the plugin
version info wasn&amp;rsquo;t getting committed correctly. I had committed
everything to git after using the suggested &lt;code>.gitignore&lt;/code> and pushed to
my git remote. But if I cloned that repo to a different area and
attempted to start the Jenkins server from there, it crashed with this
message:&lt;/p>
&lt;p>&lt;a id="code-snippet--jenkins-crash-log">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">2022-07-15 13:07:06.082+0000 [id=31] SEVERE jenkins.InitReactorRunner$1#onTaskFailed: Failed Loading global config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">com.thoughtworks.xstream.mapper.CannotResolveClassException: hudson.security.ProjectMatrixAuthorizationStrategy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:81)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:55)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:79)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:74)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:71)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.XStream2$CompatibilityMapper.realClass(XStream2.java:411)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.xstream.MapperDelegate.realClass(MapperDelegate.java:46)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.RobustReflectionConverter.determineType(RobustReflectionConverter.java:521)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> at hudson.util.RobustReflectionConverter.doUnmarshal(RobustReflectionConverter.java:346)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Caused: jenkins.util.xstream.CriticalXStreamException:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">---- Debugging information ----
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cause-exception : com.thoughtworks.xstream.mapper.CannotResolveClassException
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cause-message : hudson.security.ProjectMatrixAuthorizationStrategy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">class : hudson.model.Hudson
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">required-type : hudson.model.Hudson
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">converter-type : hudson.util.RobustReflectionConverter
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">path : /hudson/authorizationStrategy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">line number : 12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">version : not available
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-------------------------------
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--jenkins-crash-log">Code Snippet 1&lt;/a>:&lt;/span>
Snippet of Jenkins crash when attempting to run the server from the freshly cloned git repo
&lt;/div>
&lt;h2 id="jenkins-plugin-manager">Jenkins Plugin Manager&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#jenkins-plugin-manager">#&lt;/a>&lt;/h2>
&lt;p>So I &lt;a href="https://community.jenkins.io/t/version-controlling-jenkins-config-help-defining-a-gitignore-that-minimizes-the-git-repo-size/3036">reached out for help&lt;/a> on the Jenkins Community. One of the key
contributors to Jenkins, &lt;a href="https://community.jenkins.io/u/MarkEWaite">Mark Waite&lt;/a>, was tremendously helpful. He
suggested using his &lt;a href="https://github.com/jenkinsci/plugin-installation-manager-tool">&lt;code>jenkins-plugin-manager&lt;/code>&lt;/a> tool. After trying it out
for a bit, I realized that this tool had everything I needed for
version controlling the plugin versions:&lt;/p>
&lt;ul>
&lt;li>Ability to save a list of installed Jenkins plugins and their
versions to a file.&lt;/li>
&lt;li>Ability to batch install all the plugins of the versions listed in a
file.&lt;/li>
&lt;/ul>
&lt;p>This was like doing Python&amp;rsquo;s &lt;a href="https://scripter.co/saving-python-pip-dependencies/">plugin management using
&lt;code>requirements.txt&lt;/code>&lt;/a>, except that this was for Jenkins.&lt;/p>
&lt;h2 id="full-solution">Full solution&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#full-solution">#&lt;/a>&lt;/h2>
&lt;p>With a combination of the &lt;code>.gitignore&lt;/code> that I started with from that
SO answer, managing plugins using &lt;code>jenkins-plugin-manager&lt;/code>, tweaking
the &lt;code>.gitignore&lt;/code> to my liking, and adding helper Bash scripts for
downloading and running Jenkins server binaries, and doing the plugin
management, I finally got what I needed:&lt;/p>
&lt;div class="org-center">
&lt;p>&lt;a href="https://github.com/kaushalmodi/jenkins-minimal">https://github.com/kaushalmodi/jenkins-minimal&lt;/a>&lt;/p>
&lt;/div>
&lt;p>The README on the repo has all the instructions.&lt;/p></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/tags/git">git</category><category domain="https://scripter.co/tags/jenkins">jenkins</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/version-controlling-jenkins-config/</guid><pubDate>Wed, 20 Jul 2022 00:18:00 -0400</pubDate></item><item><title>Using Git Delta with Magit</title><link>https://scripter.co/using-git-delta-with-magit/</link><description>&lt;blockquote>&lt;em>Git Delta&lt;/em> is a command line utility that beautifies git diffs in the
terminal. But did you know that it can do the same in Magit too?&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#installing-delta">Installing &lt;code>delta&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#installing-magit-delta">Installing &lt;code>magit-delta&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#configuring-delta">Configuring &lt;code>delta&lt;/code>&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>&lt;a href="https://github.com/dandavison/delta">Delta&lt;/a> is a highly configurable
&lt;span class="sidenote-number">&lt;small class="sidenote">
I am not kidding. Check out the output of &lt;a href="https://dandavison.github.io/delta/full---help-output.html">&lt;code>delta --help&lt;/code>&lt;/a>.
&lt;/small>&lt;/span>
command line utility that makes the git diffs look better, while also
syntax-highlighting
&lt;span class="sidenote-number">&lt;small class="sidenote">
&lt;a href="https://github.com/Wilfred/difftastic/">Difftastic&lt;/a> is another popular diff tool which compares files based on
their syntax. I like reviewing &lt;em>git diffs&lt;/em> from within Emacs
(Magit). But &lt;em>difftastic&lt;/em> &lt;a href="https://github.com/Wilfred/difftastic/issues/251">does not support Magit&lt;/a>.
&lt;/small>&lt;/span>
the code in the diffs.&lt;/p>
&lt;p>When I first heard of &amp;ldquo;syntax highlighted diffs&amp;rdquo;, I wasn&amp;rsquo;t sure what
that meant. If you are in the same boat, here&amp;rsquo;s a screenshot that
shows that.&lt;/p>
&lt;p>&lt;a id="figure--git-delta-example">&lt;/a>&lt;/p>
&lt;figure>
&lt;a href="delta-example.png">
&lt;img src="https://scripter.co/using-git-delta-with-magit/delta-example.png" alt="Figure 1: Example of how delta renders a git diff for an ox-hugo commit"/> &lt;/a>&lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>Example of how &lt;code>delta&lt;/code> renders a &lt;em>git diff&lt;/em> for an &lt;code>ox-hugo&lt;/code> commit
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;p>But I do most of my git operations including viewing of diffs from
within Emacs, using &lt;a href="https://magit.vc">Magit&lt;/a>.&lt;/p>
&lt;div class="verse">
&lt;p>        .. and thankfully &lt;em>delta&lt;/em> &lt;a href="https://dandavison.github.io/delta/using-delta-with-magit.html">works with Magit&lt;/a>!&lt;br />&lt;/p>
&lt;/div>
&lt;p>Below screenshot shows how the same diff looks like in Magit.&lt;/p>
&lt;p>&lt;a id="figure--git-magit-delta-example">&lt;/a>&lt;/p>
&lt;figure>
&lt;a href="magit-delta-example.png">
&lt;img src="https://scripter.co/using-git-delta-with-magit/magit-delta-example.png" alt="Figure 2: Example of how magit-delta renders a git diff for an ox-hugo commit"/> &lt;/a>&lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 2: &lt;/span>Example of how &lt;code>magit-delta&lt;/code> renders a &lt;em>git diff&lt;/em> for an &lt;code>ox-hugo&lt;/code> commit
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;p>The &lt;a href="https://github.com/dandavison/magit-delta">&lt;code>magit-delta&lt;/code>&lt;/a> Emacs package makes this possible, which is also
developed by the &lt;code>delta&lt;/code> author Dan Davison.&lt;/p>
&lt;dl>
&lt;dt>Caveat&lt;/dt>
&lt;dd>If the line numbers are enabled in &lt;code>delta&lt;/code>, they mess up
the interactive expanding and collapsing of diffs in Magit. See
&lt;a href="https://github.com/dandavison/magit-delta/issues/13#issuecomment-949820122">Magit Delta Issue # 13&lt;/a> for more details.&lt;/dd>
&lt;/dl>
&lt;p>Now, I am alright with not seeing the line numbers in Magit. But I
really liked to see the line numbers in the side-by-side view in the
terminal. Luckily, if disabled the &lt;code>line-numbers&lt;/code> feature but enabled
the &lt;code>side-by-side&lt;/code> view, I got what I wanted!&lt;/p>
&lt;ol>
&lt;li>Line numbers are disabled in Magit and expanding/collapsing of
diffs works correctly. &lt;em>I am also really glad that I don&amp;rsquo;t see the
side-by-side view in Magit diffs even when I enable that feature in
&lt;code>delta&lt;/code>, because I like to have my Emacs buffers only about 90
characters wide.&lt;/em>&lt;/li>
&lt;li>Line numbers &lt;strong>and&lt;/strong> side-by-side view are enabled in the terminal.&lt;/li>
&lt;/ol>
&lt;p>I&amp;rsquo;ll end this post with pointers to installing &lt;code>delta&lt;/code> and
&lt;code>magit-delta&lt;/code> and how to configure them.&lt;/p>
&lt;h2 id="installing-delta">Installing &lt;code>delta&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#installing-delta">#&lt;/a>&lt;/h2>
&lt;p>You can install &lt;code>delta&lt;/code> (it&amp;rsquo;s called &lt;code>git-delta&lt;/code> in some package
managers) using one of the methods listed &lt;a href="https://dandavison.github.io/delta/installation.html">in its manual&lt;/a>, or you can
download → extract its statically compiled binary for your OS from its
&lt;a href="https://github.com/dandavison/delta/releases">GitHub Releases&lt;/a> page.&lt;/p>
&lt;h2 id="installing-magit-delta">Installing &lt;code>magit-delta&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#installing-magit-delta">#&lt;/a>&lt;/h2>
&lt;p>Once you put this snippet in your Emacs config and evaluate it, it
will install this package and enable the &lt;code>magit-delta-mode&lt;/code> in the
Magit buffers.&lt;/p>
&lt;p>&lt;a id="code-snippet--enabling-magit-delta">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nb">use-package&lt;/span> &lt;span class="nv">magit-delta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">:ensure&lt;/span> &lt;span class="no">t&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">:hook&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">magit-mode&lt;/span> &lt;span class="o">.&lt;/span> &lt;span class="nv">magit-delta-mode&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--enabling-magit-delta">Code Snippet 1&lt;/a>:&lt;/span>
Installing and enabling &lt;code>magit-delta&lt;/code> using &lt;code>use-package&lt;/code>
&lt;/div>
&lt;h2 id="configuring-delta">Configuring &lt;code>delta&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#configuring-delta">#&lt;/a>&lt;/h2>
&lt;p>Here&amp;rsquo;s a snippet for &lt;code>delta&lt;/code> configuration from my &lt;code>.gitconfig&lt;/code>. It&amp;rsquo;s
mostly the same as the one in &lt;em>delta&lt;/em>&amp;rsquo;s the &lt;a href="https://github.com/dandavison/delta#get-started">Getting Started&lt;/a> guide. The
main difference in my workaround for the &lt;code>magit-delta&lt;/code> issue.&lt;/p>
&lt;p>&lt;a id="code-snippet--delta-gitconfig">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>core&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">pager&lt;/span> &lt;span class="o">=&lt;/span> delta
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>interactive&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">diffFilter&lt;/span> &lt;span class="o">=&lt;/span> delta --color-only
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>add.interactive&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">useBuiltin&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">false&lt;/span> &lt;span class="c1"># required for git 2.37.0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>diff&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">colorMoved&lt;/span> &lt;span class="o">=&lt;/span> default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>delta&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># https://github.com/dandavison/magit-delta/issues/13&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># line-numbers = true # Don&amp;#39;t do this.. messes up diffs in magit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> side-by-side &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span> &lt;span class="c1"># Display a side-by-side diff view instead of the traditional view&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># navigate = true # Activate diff navigation: use n to jump forwards and N to jump backwards&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> relative-paths &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span> &lt;span class="c1"># Output all file paths relative to the current directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> file-style &lt;span class="o">=&lt;/span> yellow
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> hunk-header-style &lt;span class="o">=&lt;/span> line-number syntax
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--delta-gitconfig">Code Snippet 2&lt;/a>:&lt;/span>
My configuration for &lt;code>delta&lt;/code> in &lt;code>.gitconfig&lt;/code>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/emacs">emacs</category><category domain="https://scripter.co/tags/git">git</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><category domain="https://scripter.co/tags/magit">magit</category><category domain="https://scripter.co/tags/diff">diff</category><guid>https://scripter.co/using-git-delta-with-magit/</guid><pubDate>Wed, 06 Jul 2022 22:04:00 -0400</pubDate></item><item><title>Gujarati Transliteration</title><link>https://scripter.co/gujarati-transliteration/</link><description>&lt;blockquote>You can phonetically write a non-English language on an English
keyword in Emacs, and that transforms into that non-English
script. This is called &lt;a href="https://en.wikipedia.org/wiki/Transliteration">&lt;em>transliteration&lt;/em>&lt;/a>, and I demonstrate that for
the Gujarati language in this post.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#enabling-transliteration">Enabling transliteration&lt;/a>&lt;/li>
&lt;li>&lt;a href="#toggling-the-input-method">Toggling the input method&lt;/a>&lt;/li>
&lt;li>&lt;a href="#caveats-with-gujarati-and-other-indic-language-transliteration">Caveats with Gujarati and other Indic language transliteration&lt;/a>&lt;/li>
&lt;li>&lt;a href="#input-method-cheat-sheet">&amp;ldquo;Input method&amp;rdquo; cheat sheet&lt;/a>&lt;/li>
&lt;li>&lt;a href="#closing">Closing&lt;/a>&lt;/li>
&lt;li>&lt;a href="#gujarati-transliteration-references">References&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>Emacs provides the transliteration feature using the
&lt;strong>set-input-method&lt;/strong> command. I&amp;rsquo;ll introduce that and few related
functions in this post to get to help get started with transliteration
quickly.&lt;/p>
&lt;h2 id="enabling-transliteration">Enabling transliteration&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#enabling-transliteration">#&lt;/a>&lt;/h2>
&lt;p>Emacs uses the &amp;ldquo;input method&amp;rdquo; feature to do character conversion from
ASCII to the target language or script. The &amp;ldquo;input method&amp;rdquo;, stored in
&lt;code>current-input-method&lt;/code>, is &lt;em>nil&lt;/em> by default. In this state, you see
the exact ASCII in Emacs buffer, that you typed on the
keyboard&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>.&lt;/p>
&lt;p>In this post, my target non-English language is &lt;a href="https://en.wikipedia.org/wiki/Gujarati_language">Gujarati&lt;/a>. So I want to
type on my English keyboard and have Gujarati script letters show up
in the buffer.&lt;/p>
&lt;p>Emacs provides the &lt;code>set-input-method&lt;/code> command to change the &lt;em>current
input method&lt;/em>. This command is bound to &lt;kbd>C-x&lt;/kbd> &lt;kbd>RET&lt;/kbd>
&lt;kbd>C-\&lt;/kbd> by default. Pick the new input method after calling
that command.&lt;/p>
&lt;div class="note">
&lt;p>To see the available input methods, do &lt;code>M-x list-input-methods&lt;/code>.&lt;/p>
&lt;/div>
&lt;p>As I want to do Gujarati transliteration, I pick the &lt;code>gujarati-itrans&lt;/code>
method.&lt;/p>
&lt;p>If you don&amp;rsquo;t know Gujarati, don&amp;rsquo;t fret! The commands shown here will
work when transliterating to other languages too &amp;mdash; only the
Gujarati-specific &lt;em>input method&lt;/em> &lt;code>gujarati-itrans&lt;/code> will change to the
input method of your choice.&lt;/p>
&lt;h2 id="toggling-the-input-method">Toggling the input method&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#toggling-the-input-method">#&lt;/a>&lt;/h2>
&lt;p>I often need to switch between the Gujarati and English languages in
the same document. You can see me doing that in this post next section
onwards. The &lt;code>toggle-input-method&lt;/code> command bound by default to
&lt;kbd>C-\&lt;/kbd> is helpful here.&lt;/p>
&lt;p>So if I am already in the &amp;ldquo;Gujarati transliteration mode&amp;rdquo; calling this
command will set &lt;code>current-input-method&lt;/code> back to &lt;em>nil&lt;/em>. Repeating that
same call will again set &lt;code>current-input-method&lt;/code> to &lt;code>gujarati-itrans&lt;/code>,
and I will once again be in the &amp;ldquo;Gujarati transliteration mode&amp;rdquo;.&lt;/p>
&lt;h2 id="caveats-with-gujarati-and-other-indic-language-transliteration">Caveats with Gujarati and other Indic language transliteration&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#caveats-with-gujarati-and-other-indic-language-transliteration">#&lt;/a>&lt;/h2>
&lt;p>Apologies, but this section is meaningful only if you know how to read
Gujarati. So you can safely skip to the next section.&lt;/p>
&lt;p>Below table is a quick glimpse of some nuances in Gujarati
transliteration. I will save my explanation and instead show some of
the mistakes I made in transliteration using examples.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>ASCII input&lt;/th>
&lt;th>Gujarati Transliteration&lt;/th>
&lt;th>Rough Translation&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;em>ram&lt;/em>&lt;/td>
&lt;td>રમ્&lt;/td>
&lt;td>(incorrect spelling, no meaning)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>rama&lt;/em>&lt;/td>
&lt;td>રમ&lt;/td>
&lt;td>play&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>raama&lt;/em>&lt;/td>
&lt;td>રામ&lt;/td>
&lt;td>a popular name Raama (as in Lord Raama)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>angreji&lt;/em>&lt;/td>
&lt;td>અન્ગ્રેજિ&lt;/td>
&lt;td>(incorrect spelling)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>hu.n&lt;/em>&lt;/td>
&lt;td>હું&lt;/td>
&lt;td>I&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>chhu.n&lt;/em>&lt;/td>
&lt;td>છું&lt;/td>
&lt;td>am&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>a.ngrejii&lt;/em>&lt;/td>
&lt;td>અંગ્રેજી&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&lt;/td>
&lt;td>English (language)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>Ime.cksa&lt;/em>&lt;/td>
&lt;td>ઈમૅક્સ&lt;/td>
&lt;td>this literally reads &amp;ldquo;Emacs&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="input-method-cheat-sheet">&amp;ldquo;Input method&amp;rdquo; cheat sheet&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#input-method-cheat-sheet">#&lt;/a>&lt;/h2>
&lt;p>Thankfully Emacs provides full help through the
&lt;code>describe-input-method&lt;/code> command bound to &lt;kbd>C-h&lt;/kbd>
&lt;kbd>C-\&lt;/kbd>
&lt;span class="sidenote-number">&lt;small class="sidenote">
If you haven&amp;rsquo;t already noticed the consistency in these bindings, the
default bindings with &lt;kbd>C-\&lt;/kbd> in them are related to &amp;ldquo;input
method&amp;rdquo; commands.
&lt;/small>&lt;/span>
by default.&lt;/p>
&lt;p>For example, &lt;code>M-x describe-input-method gujarati-itrans&lt;/code> gives this:&lt;/p>
&lt;p>&lt;a id="figure--gujarati-itrans-help">&lt;/a>&lt;/p>
&lt;figure>
&lt;a href="gujarati-itrans-help.png">
&lt;img src="https://scripter.co/gujarati-transliteration/gujarati-itrans-help.png" alt="Figure 1: Partial screen capture of Gujarati transliteration cheat sheet C-h C- gujarati-itrans"/> &lt;/a>&lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>Partial screen capture of Gujarati transliteration cheat sheet &lt;code>C-h C- gujarati-itrans&lt;/code>
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;p>The ∗Help∗ that shows up looks formidable at the first glance. Though,
I found comfort in the fact that roughly half of the key sequences
were obvious and roughly half resulted in Gujarati characters that I
have never found the need of! 😃&lt;/p>
&lt;h2 id="closing">Closing&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#closing">#&lt;/a>&lt;/h2>
&lt;p>Typing this in the &amp;ldquo;transliteration mode&amp;rdquo;:&lt;/p>
&lt;blockquote>
&lt;p>maaru naama kaushala chhe. mane e jaaNii ne aana.nda thaaya chhe ke
hu.n aa sahelaaI thI lakhI shaku chhu.n. (joDanI-bhula maapha.)&lt;/p>
&lt;/blockquote>
&lt;p>will result in:&lt;/p>
&lt;p>મારુ નામ કૌશલ છે. મને એ જાણી ને આનંદ થાય છે કે હું આ સહેલાઈ થી લખી શકુ
છું. (જોડણી-ભુલ માફ.)&lt;/p>
&lt;p>&lt;em>Translation: My name is Kaushal. I am happy knowing that I can write
this easily.&lt;/em>&lt;/p>
&lt;h2 id="gujarati-transliteration-references">References&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#gujarati-transliteration-references">#&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Input-Methods.html" title="Emacs Lisp: (info &amp;quot;(emacs) Input Methods&amp;quot;)">Emacs Info: Input Methods&lt;/a>
&lt;ul>
&lt;li>&lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Select-Input-Method.html" title="Emacs Lisp: (info &amp;quot;(emacs) Select Input Method&amp;quot;)">Emacs Info: Select Input Method&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>M-x describe-input-method gujarati-itrans&lt;/code>&lt;/li>
&lt;/ul>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>I am assuming an English keyboard here.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>This spelling might still render incorrectly on your browser
depending on the unicode character set available for Gujarati on your
system.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/emacs">emacs</category><category domain="https://scripter.co/series/gujarati-in-emacs">Gujarati in Emacs</category><category domain="https://scripter.co/tags/gujarati">gujarati</category><category domain="https://scripter.co/tags/transliteration">transliteration</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/gujarati-transliteration/</guid><pubDate>Mon, 27 Jun 2022 18:43:00 -0400</pubDate></item><item><title>Cleaning up ${GOPATH}/pkg/</title><link>https://scripter.co/cleaning-up-gopath-pkg/</link><description>&lt;blockquote>Use &lt;code>go clean -modcache&lt;/code> to clean up all the old auto-downloaded Go
modules in &lt;code>${GOPATH}/pkg/&lt;/code>. That&amp;rsquo;s all you need to know. Rest of the
post gives just the history of how I got there.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#the-gopath-directory">The &lt;code>$GOPATH&lt;/code> directory&lt;/a>&lt;/li>
&lt;li>&lt;a href="#the-disk-space-issue">The disk space issue&lt;/a>&lt;/li>
&lt;li>&lt;a href="#write-protected-gopath-pkg">Write-protected &lt;code>${GOPATH}/pkg/&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#cleaning-up-modcache">Cleaning up &amp;ldquo;modcache&amp;rdquo;&lt;/a>&lt;/li>
&lt;li>&lt;a href="#wrapping-up">Wrapping up&lt;/a>&lt;/li>
&lt;li>&lt;a href="#references">References&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>As I mentioned in the post description above, the solution to clean up
the auto-populated &lt;code>${GOPATH}/pkg/&lt;/code> is to run &lt;code>go clean -modcache&lt;/code>. While the solution is simple, it wasn&amp;rsquo;t easy for me to
discover, and so I am writing this short piece to make it a bit more
discoverable for others like me.&lt;/p>
&lt;h2 id="the-gopath-directory">The &lt;code>$GOPATH&lt;/code> directory&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#the-gopath-directory">#&lt;/a>&lt;/h2>
&lt;p>The &lt;code>GOPATH&lt;/code> environment variable lists places to look for Go code.&lt;/p>
&lt;p>If you haven&amp;rsquo;t set this variable and if the &lt;code>${HOME}/go/&lt;/code> directory
isn&amp;rsquo;t used to contain the Go distribution, &lt;code>$GOPATH&lt;/code> defaults to that
path.&lt;/p>
&lt;p>When you run &lt;code>go get&lt;/code> to install any Go package,&lt;/p>
&lt;ul>
&lt;li>&lt;code>${GOPATH}/pkg/&lt;/code> gets populated with all the Go module dependencies,&lt;/li>
&lt;li>.. and all package&amp;rsquo;s executables if any get installed to &lt;code>${GOPATH}/bin/&lt;/code>.&lt;/li>
&lt;/ul>
&lt;div class="note">
&lt;p>I think that if the user has set the &lt;code>$GOMODCACHE&lt;/code> environment
variable, that directory would get populated with Go module downloads
instead. But for my use, I am sticking with only the &lt;code>$GOPATH&lt;/code> for
simplicity.&lt;/p>
&lt;/div>
&lt;p>To confirm the value of &lt;code>GOPATH&lt;/code> used by Go, see &lt;code class="code-inline language-shell">go env &lt;span class="p">|&lt;/span> rg GOPATH&lt;/code>.&lt;/p>
&lt;h2 id="the-disk-space-issue">The disk space issue&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#the-disk-space-issue">#&lt;/a>&lt;/h2>
&lt;p>I have been happily installing and building Go apps like Hugo using
&lt;code>go get&lt;/code> or &lt;code>go build&lt;/code>, and all these installations would end up in
the &lt;code>$GOPATH&lt;/code>. But over time, I noticed that the disk space used by
&lt;code>${GOPATH}/pkg/&lt;/code> just kept on creeping up.&lt;/p>
&lt;p>Today I happened to notice that this directory was taking up roughly
&lt;strong>4 GB&lt;/strong> of my disk space! I started analyzing why it was taking up so
much space using my favorite tool for this purpose &amp;ndash; &lt;a href="https://dev.yorhel.nl/ncdu">ncdu&lt;/a>.&lt;/p>
&lt;p>Here&amp;rsquo;s a snapshot showing disk usage by one of the sub-directories
under &lt;code>${GOPATH}/pkg/&lt;/code>, which shows the problem &amp;mdash; Over time, I had
accumulated multiple versions of multiple packages!&lt;/p>
&lt;p>&lt;a id="figure--go-pkg-disk-usage">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/cleaning-up-gopath-pkg/go-pkg-disk-usage.png" alt="Figure 1: Snapshot of ncdu showing disk usage for a ${GOPATH}/pkg/ directory"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>Snapshot of &lt;em>ncdu&lt;/em> showing disk usage for a &lt;code>${GOPATH}/pkg/&lt;/code> directory
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="write-protected-gopath-pkg">Write-protected &lt;code>${GOPATH}/pkg/&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#write-protected-gopath-pkg">#&lt;/a>&lt;/h2>
&lt;p>I identified the problem. And I knew that I just needed to delete all
the old packages.&lt;/p>
&lt;div class="verse">
&lt;p>        But Go wouldn&amp;rsquo;t allow deleting those &lt;code>pkg/&lt;/code> directories!&lt;br />&lt;/p>
&lt;/div>
&lt;p>I had been searching a solution to this on and off, but didn&amp;rsquo;t have
much success, mainly attributed to the short and generic name of the
&amp;ldquo;Go&amp;rdquo; language, and the fact that I didn&amp;rsquo;t know what the
&lt;code>${GOPATH}/pkg/&lt;/code> directory was called.&lt;/p>
&lt;h2 id="cleaning-up-modcache">Cleaning up &amp;ldquo;modcache&amp;rdquo;&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#cleaning-up-modcache">#&lt;/a>&lt;/h2>
&lt;p>Today, I finally had success when I searched for this magic phrase:
&lt;em>golang &amp;ldquo;cannot remove&amp;rdquo; &amp;ldquo;pkg/mod&amp;rdquo;&lt;/em> .. and &lt;a href="https://github.com/golang/go/issues/27161#issuecomment-415213240">Golang Issues # 27161&lt;/a> was
the first search result!&lt;/p>
&lt;p>The solution was so simple, but so difficult to look for ..&lt;/p>
&lt;p>&lt;a id="code-snippet--go-clean-modcache">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">go clean -modcache
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--go-clean-modcache">Code Snippet 1&lt;/a>:&lt;/span>
Command to delete contents in &lt;code>${GOPATH}/pkg/&lt;/code> or the Go &lt;i>modcache&lt;/i>
&lt;/div>
&lt;p>From that issue, I also learned that the &lt;code>${GOPATH}/pkg/&lt;/code> directory is
the default &amp;ldquo;modcache&amp;rdquo; or the cache directory for holding all the
installed Go modules.&lt;/p>
&lt;h2 id="wrapping-up">Wrapping up&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#wrapping-up">#&lt;/a>&lt;/h2>
&lt;p>Knowing that the stuff I was trying to delete is called &lt;em>modcache&lt;/em>,
this of course works ..&lt;/p>
&lt;p>&lt;a id="code-snippet--go-help-clean">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">go &lt;span class="nb">help&lt;/span> clean &lt;span class="p">|&lt;/span> rg modcache -A &lt;span class="m">2&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--go-help-clean">Code Snippet 2&lt;/a>:&lt;/span>
Getting help with cleaning up the &lt;i>modcache&lt;/i> from &lt;code>go help&lt;/code>
&lt;/div>
&lt;p>The issue referenced above gets resolved when a &lt;a href="https://github.com/golang/go/issues/27161#issuecomment-625899357">&lt;code>-modcacherw&lt;/code> switch
gets added&lt;/a> to the &lt;code>go build&lt;/code> command. I see that switch when I run
&lt;code>go help build&lt;/code>. &lt;em>But that switch is not available for &lt;code>go get&lt;/code>?
.. because I don&amp;rsquo;t see it when I run &lt;code>go help get&lt;/code>.&lt;/em>&lt;/p>
&lt;p>I don&amp;rsquo;t understand why Go decided to make this so complicated by
taking away the &lt;em>write&lt;/em> access from the user who installed the Go
package!&lt;/p>
&lt;div class="verse">
&lt;p>        At least &lt;code>go clean -modcache&lt;/code> accomplishes what I want .. &lt;em>sigh&lt;/em>&lt;br />&lt;/p>
&lt;/div>
&lt;h2 id="references">References&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#references">#&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://pkg.go.dev/cmd/go#hdr-GOPATH_environment_variable">&lt;code>go&lt;/code> Documentation &amp;ndash; &lt;code>GOPATH&lt;/code> environment variable&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://pkg.go.dev/cmd/go#hdr-Remove_object_files_and_cached_files">&lt;code>go&lt;/code> Documentation &amp;ndash; &lt;code>go help clean&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/golang/go/wiki/SettingGOPATH">Go Wiki &amp;ndash; Setting &lt;code>GOPATH&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/golang/go/issues/27161">Golang Issue # 27161&lt;/a>&lt;/li>
&lt;/ul></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/programming">programming</category><category domain="https://scripter.co/tags/golang">golang</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/cleaning-up-gopath-pkg/</guid><pubDate>Sat, 25 Jun 2022 10:48:00 -0400</pubDate></item><item><title>View GitHub Pull Requests in Magit</title><link>https://scripter.co/view-github-pull-requests-in-magit/</link><description>&lt;blockquote>How to view GitHub Pull Request branches locally in the cloned repo,
and more importantly, how to do that automatically from within Emacs.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#locally-creating-a-branch-for-a-pr">Locally creating a branch for a PR&lt;/a>&lt;/li>
&lt;li>&lt;a href="#getting-references-to-all-pull-requests">Getting references to all Pull Requests&lt;/a>&lt;/li>
&lt;li>&lt;a href="#automatically-adding-pr-refs">Automatically adding PR refs&lt;/a>&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;li>&lt;a href="#references">References&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I have a few public projects in git repos, but I don&amp;rsquo;t get that much
traffic in &lt;em>Pull Requests (PR)&lt;/em>
&lt;span class="sidenote-number">&lt;small class="sidenote">
Gitlab calls these &lt;em>Merge Requests&lt;/em> or MRs.
&lt;/small>&lt;/span>
. So when I need to add additional commits to a PR, I would just add
the PR author&amp;rsquo;s &lt;em>remote&lt;/em> to my local repo, &lt;em>check out&lt;/em> their PR
branch, add my own commits and then merge that to my project&amp;rsquo;s &lt;em>main&lt;/em>
branch.&lt;/p>
&lt;p>As these occurrences were few and far apart, I didn&amp;rsquo;t have a need to
view the &lt;em>PR branches&lt;/em> directly from within Emacs/Magit. Though, I
somehow knew that each GitHub Pull Request&amp;rsquo;s &lt;em>HEAD&lt;/em> got assigned a &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-References">git
&lt;strong>reference&lt;/strong>&lt;/a>. But I didn&amp;rsquo;t need to use that knowledge until today
😃.&lt;/p>
&lt;p>Today, when discussing &lt;a href="https://github.com/protesilaos/denote/pull/20">PR # 20&lt;/a> on &lt;a href="https://protesilaos.com/">Prot&amp;rsquo;s&lt;/a> &lt;a href="https://protesilaos.com/emacs/denote">Denote&lt;/a> package&amp;rsquo;s GitHub
mirror
&lt;span class="sidenote-number">&lt;small class="sidenote">
Prot uses &lt;a href="https://git.sr.ht/~protesilaos/denote">SourceHut&lt;/a> as the primary git forge for his Emacs
packages. But I am glad that he doesn&amp;rsquo;t mind the activity in Issues
and Pull Requests on the GitHub mirror.
&lt;/small>&lt;/span>
, he wrote &lt;a href="https://github.com/protesilaos/denote/pull/20#issuecomment-1164676013">this comment&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>Now I just need to figure out how best to incorporate your changes
into the &lt;code>org-id&lt;/code> branch so I can add the final bits. I am not too
familiar with the PR workflow &amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>.. and that inspired this post today.&lt;/p>
&lt;h2 id="locally-creating-a-branch-for-a-pr">Locally creating a branch for a PR&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#locally-creating-a-branch-for-a-pr">#&lt;/a>&lt;/h2>
&lt;p>From the &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally">GitHub docs&lt;/a>, the &lt;code>git&lt;/code> command to create a local branch for a
PR is this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git fetch origin pull/ID/head:BRANCHNAME
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I have the &lt;code>denote&lt;/code> package cloned from its GitHub mirror. So the
&lt;strong>origin&lt;/strong> remote&amp;rsquo;s &lt;strong>url&lt;/strong> is &lt;code>https://github.com/protesilaos/denote&lt;/code>.&lt;/p>
&lt;div class="note">
&lt;p>Make sure that the remote name used in this command is pointing to a
GitHub repo, and not a mirror forge like GitLab or SourceHut.&lt;/p>
&lt;/div>
&lt;p>When I ran the below command, I got a new branch &lt;strong>pr-20&lt;/strong> pointing to
the latest commit of that PR:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">git fetch origin pull/20/head:pr-20
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Awesome!&lt;/p>
&lt;div class="verse">
&lt;p>.. But that wasn&amp;rsquo;t good enough&lt;br />
    .. Now I wanted more&lt;br />
        .. I didn&amp;rsquo;t want to manually create a branch for each PR.&lt;br />&lt;/p>
&lt;/div>
&lt;h2 id="getting-references-to-all-pull-requests">Getting references to all Pull Requests&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#getting-references-to-all-pull-requests">#&lt;/a>&lt;/h2>
&lt;p>Now that I was on that quest of &amp;ldquo;I want more&amp;rdquo;, it didn&amp;rsquo;t take me long
to re-discover &lt;a href="https://oremacs.com/2015/03/11/git-tricks/#illusion-2-quickly-get-github-pull-requests-on-your-system">this 7-year old nugget&lt;/a> by &lt;a href="https://oremacs.com">Oleh Krehel&lt;/a>. Here are the
relevant bits from that post:&lt;/p>
&lt;ol>
&lt;li>Open the local repo&amp;rsquo;s &lt;code>.git/config&lt;/code> file.&lt;/li>
&lt;li>Find the &lt;code>[remote &amp;quot;origin&amp;quot;]&lt;/code> section&lt;/li>
&lt;li>Modify it by adding this one line with &lt;strong>pull&lt;/strong> refs. &lt;em>This is the
same for all GitHub repositories.&lt;/em>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cfg" data-lang="cfg">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[remote &amp;#34;origin&amp;#34;]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">https://github.com/USER/REPO.git
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> fetch = +refs/heads/*:refs/remotes/origin/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl">&lt;span class="s"> fetch = +refs/pull/*/head:refs/pull/origin/*&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>With that edit in place, when I did &lt;kbd>l&lt;/kbd> &lt;kbd>a&lt;/kbd> (show
the log for all git references), followed by &lt;kbd>f&lt;/kbd> &lt;kbd>a&lt;/kbd>
(fetch all the remotes) in the Magit, I could see the references to
the &lt;code>denote&lt;/code> repo&amp;rsquo;s PRs!&lt;/p>
&lt;p>&lt;a id="figure--magit-denote-pr-refs">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/view-github-pull-requests-in-magit/pr-20-ref.png" alt="Figure 1: Viewing PR references from denote package&amp;rsquo;s GitHub repo"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>Viewing PR references from &lt;code>denote&lt;/code> package&amp;rsquo;s GitHub repo
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;div class="verse">
&lt;p>.. But that still wasn&amp;rsquo;t good enough&lt;br />
    .. I didn&amp;rsquo;t want to manually edit the &lt;code>.git/config&lt;/code> in each repo.&lt;br />&lt;/p>
&lt;/div>
&lt;h2 id="automatically-adding-pr-refs">Automatically adding PR refs&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#automatically-adding-pr-refs">#&lt;/a>&lt;/h2>
&lt;p>Of course, I wasn&amp;rsquo;t the first one to think of this!&lt;/p>
&lt;p>Another Emacs veteran &lt;a href="https://endlessparentheses.com/">Artur Malabarba&lt;/a> had already had this covered
also around &lt;a href="https://endlessparentheses.com/automatically-configure-magit-to-access-github-prs.html">7 years back&lt;/a>. Coincidentally, that post was written as a
response to that same blog post by Oleh where he shared the above
&lt;code>.git/config&lt;/code> tip.&lt;/p>
&lt;p>In that post, Artur shares an Emacs Lisp function that uses Magit
functions like &lt;code>magit-get&lt;/code>, &lt;code>magit-get-all&lt;/code> and &lt;code>magit-git-string&lt;/code> to
auto-add the &lt;code>fetch = +refs/pull/*/head:refs/pull/origin/*&lt;/code> line in
the &lt;code>.git/config&lt;/code>. This magic happens after checking that the &lt;strong>origin&lt;/strong>
remote points to a GitHub repo, and if that line doesn&amp;rsquo;t already
exist.&lt;/p>
&lt;p>Here, I am lightly modifying the function shared in that post so that
the &lt;strong>origin&lt;/strong> remote name is not hard-coded
&lt;span class="sidenote-number">&lt;small class="sidenote">
The reason is that sometimes, I name the original remote as &lt;strong>upstream&lt;/strong>
and my fork as &lt;strong>fork&lt;/strong>, and I might have no remote named &lt;strong>origin&lt;/strong>.
&lt;/small>&lt;/span>
. Credit for the main logic in this code still goes to Artur.&lt;/p>
&lt;p>&lt;a id="code-snippet--add-PR-fetch-ref">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nb">defun&lt;/span> &lt;span class="nv">modi/add-PR-fetch-ref&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kp">&amp;amp;optional&lt;/span> &lt;span class="nv">remote-name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;If refs/pull is not defined on a GH repo, define it.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">If REMOTE-NAME is not specified, it defaults to the &lt;/span>&lt;span class="ss">`remote&amp;#39;&lt;/span>&lt;span class="s"> set
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">for the \&amp;#34;main\&amp;#34; or \&amp;#34;master\&amp;#34; branch.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">let*&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nv">remote-name&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">or&lt;/span> &lt;span class="nv">remote-name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">magit-get&lt;/span> &lt;span class="s">&amp;#34;branch&amp;#34;&lt;/span> &lt;span class="s">&amp;#34;main&amp;#34;&lt;/span> &lt;span class="s">&amp;#34;remote&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">magit-get&lt;/span> &lt;span class="s">&amp;#34;branch&amp;#34;&lt;/span> &lt;span class="s">&amp;#34;master&amp;#34;&lt;/span> &lt;span class="s">&amp;#34;remote&amp;#34;&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">remote-url&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">magit-get&lt;/span> &lt;span class="s">&amp;#34;remote&amp;#34;&lt;/span> &lt;span class="nv">remote-name&lt;/span> &lt;span class="s">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">fetch-refs&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">and&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">stringp&lt;/span> &lt;span class="nv">remote-url&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nf">string-match&lt;/span> &lt;span class="s">&amp;#34;github&amp;#34;&lt;/span> &lt;span class="nv">remote-url&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">magit-get-all&lt;/span> &lt;span class="s">&amp;#34;remote&amp;#34;&lt;/span> &lt;span class="nv">remote-name&lt;/span> &lt;span class="s">&amp;#34;fetch&amp;#34;&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">;; https://oremacs.com/2015/03/11/git-tricks/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">fetch-address&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">format&lt;/span> &lt;span class="s">&amp;#34;+refs/pull/*/head:refs/pull/%s/*&amp;#34;&lt;/span> &lt;span class="nv">remote-name&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">when&lt;/span> &lt;span class="nv">fetch-refs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">unless&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">member&lt;/span> &lt;span class="nv">fetch-address&lt;/span> &lt;span class="nv">fetch-refs&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">magit-git-string&lt;/span> &lt;span class="s">&amp;#34;config&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;--add&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nf">format&lt;/span> &lt;span class="s">&amp;#34;remote.%s.fetch&amp;#34;&lt;/span> &lt;span class="nv">remote-name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">fetch-address&lt;/span>&lt;span class="p">)))))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nv">add-hook&lt;/span> &lt;span class="ss">&amp;#39;magit-mode-hook&lt;/span> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">modi/add-PR-fetch-ref&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--add-PR-fetch-ref">Code Snippet 1&lt;/a>:&lt;/span>
Function to auto-add GitHub PR references to the repo's &lt;code>.git/config&lt;/code>
&lt;/div>
&lt;h2 id="summary">Summary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#summary">#&lt;/a>&lt;/h2>
&lt;p>With the above snippet added to your Emacs config and evaluated, each
time you visit a repo cloned from GitHub in the Magit Status buffer
(&lt;code>M-x magit-status&lt;/code>), the PR refs will get auto-added to that repo&amp;rsquo;s
&lt;code>.git/config&lt;/code> if needed.&lt;/p>
&lt;p>After that, you can easily view the commits from all the PRs by doing
&lt;kbd>l&lt;/kbd> &lt;kbd>a&lt;/kbd> &lt;kbd>f&lt;/kbd> &lt;kbd>a&lt;/kbd>.&lt;/p>
&lt;h2 id="references">References&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#references">#&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://oremacs.com/2015/03/11/git-tricks/#illusion-2-quickly-get-github-pull-requests-on-your-system">oremacs &amp;ndash; Some git/magit/github tricks&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://endlessparentheses.com/automatically-configure-magit-to-access-github-prs.html">endlessparentheses &amp;ndash; Automatically configure Magit to access Github PRs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally">GitHub &amp;ndash; Checking out PR branches locally&lt;/a>&lt;/li>
&lt;/ul></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/emacs">emacs</category><category domain="https://scripter.co/categories/elisp">elisp</category><category domain="https://scripter.co/tags/git">git</category><category domain="https://scripter.co/tags/magit">magit</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><category domain="https://scripter.co/tags/github">github</category><category domain="https://scripter.co/tags/git-reference">git-reference</category><guid>https://scripter.co/view-github-pull-requests-in-magit/</guid><pubDate>Thu, 23 Jun 2022 17:51:00 -0400</pubDate></item><item><title>Gujarati fonts in Emacs</title><link>https://scripter.co/gujarati-fonts-in-emacs/</link><description>&lt;blockquote>Setting a different font for a specific script or language in Emacs.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#setting-a-fontset-font">Setting a &amp;ldquo;fontset&amp;rdquo; font&lt;/a>&lt;/li>
&lt;li>&lt;a href="#gujarati-fonts">Gujarati fonts&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>All Emacs versions ship with a nifty &lt;strong>HELLO&lt;/strong> file that you can quickly
open using &lt;code>M-x view-hello-file&lt;/code> or its default binding &lt;code>C-h h&lt;/code>. This
file lists &amp;ldquo;Hello&amp;rdquo; written in dozens of languages to demonstrate some
of the character sets supported by Emacs.&lt;/p>
&lt;p>&lt;a id="figure--hello-buffer">&lt;/a>&lt;/p>
&lt;figure>
&lt;a href="hello.png">
&lt;img src="https://scripter.co/gujarati-fonts-in-emacs/hello.png" alt="Figure 1: &amp;ldquo;Hello&amp;rdquo; buffer in Emacs"/> &lt;/a>&lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>&amp;ldquo;Hello&amp;rdquo; buffer in Emacs
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;p>Born and raised in the Gujarat state in India, I grew up
speaking the &lt;a href="https://en.wikipedia.org/wiki/Gujarati_language">Gujarati (ગુજરાતી)&lt;/a> language
&lt;span class="sidenote-number">&lt;small class="sidenote">
India officially recognizes &lt;a href="https://en.wikipedia.org/wiki/Languages_of_India">22 languages&lt;/a> (as of &lt;span class="timestamp-wrapper">&lt;span class="timestamp">&amp;lt;2022-06-19 Sun&amp;gt;&lt;/span>&lt;/span>) and
Gujarati is one of them.
&lt;/small>&lt;/span>
and so it&amp;rsquo;s also the language closest to my heart. So I was pleasantly
surprised to see a representation of Gujarati in the &amp;ldquo;Hello&amp;rdquo; buffer!
In the above screenshot, in the &amp;ldquo;South Asia&amp;rdquo; section, the script after
the yellow cursor is Gujarati, and it reads &lt;em>namaste&lt;/em>.&lt;/p>
&lt;h2 id="setting-a-fontset-font">Setting a &amp;ldquo;fontset&amp;rdquo; font&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#setting-a-fontset-font">#&lt;/a>&lt;/h2>
&lt;p>It was on &lt;span class="timestamp-wrapper">&lt;span class="timestamp">&amp;lt;2018-08-13 Mon&amp;gt; &lt;/span>&lt;/span> that I discovered the presence of Gujarati
script in that &amp;ldquo;Hello&amp;rdquo; buffer, and the reason I know that exact date
is because I had &lt;a href="https://lists.gnu.org/r/help-gnu-emacs/2018-08/msg00033.html">asked a question&lt;/a> regarding that on the
&lt;em>help-gnu-emacs&lt;/em> mailing list 😃.&lt;/p>
&lt;p>This was the time when Emacs was using the &lt;a href="https://www.nongnu.org/m17n/">m17n&lt;/a> library for
multi-lingual font rendering by default. The question was regarding a
font rendering issue I was seeing. As I learn later in that thread, it
was because I didn&amp;rsquo;t have the m17n database installed on my machine
&lt;span class="sidenote-number">&lt;small class="sidenote">
At least in 2022, the &lt;a href="https://github.com/harfbuzz/harfbuzz">harfbuzz&lt;/a> library is the recommended library for
text shaping and font rendering. Someone please correct me if that&amp;rsquo;s
wrong. In any case, I have switched to using &lt;em>harfbuzz&lt;/em> instead of
&lt;em>m17n&lt;/em> for a while now and haven&amp;rsquo;t found any font-rendering issues
with non-English scripts.
&lt;/small>&lt;/span>
. But it&amp;rsquo;s in that support thread that, thanks to &lt;em>Andy Moreton&lt;/em>, I
learned that you can change the font for the Gujarati script using
&lt;code>set-fontset-font&lt;/code>.&lt;/p>
&lt;p>This applies in general to any script. You can read more details about
this function in &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Modifying-Fontsets.html" title="Emacs Lisp: (info &amp;quot;(emacs) Modifying Fontsets&amp;quot;)">Emacs Info: Modifying Fontsets&lt;/a>, but here&amp;rsquo;s the gist:&lt;/p>
&lt;p>&lt;a id="code-snippet--gujarati-set-fontset-font">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nf">set-fontset-font&lt;/span> &lt;span class="s">&amp;#34;fontset-default&amp;#34;&lt;/span> &lt;span class="ss">&amp;#39;gujarati&lt;/span> &lt;span class="s">&amp;#34;&amp;lt;FONT NAME&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--gujarati-set-fontset-font">Code Snippet 1&lt;/a>:&lt;/span>
Setting default font for Gujarati script using &lt;code>set-fontset-font&lt;/code>
&lt;/div>
&lt;p>That led me down the path of exploring the available Gujarati fonts
out there ..&lt;/p>
&lt;h2 id="gujarati-fonts">Gujarati fonts&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#gujarati-fonts">#&lt;/a>&lt;/h2>
&lt;p>After looking around for a bit, I found a wonderful &lt;a href="https://github.com/samyakbhuta/chhapkaam/wiki/%E0%AA%97%E0%AB%81%E0%AA%9C%E0%AA%B0%E0%AA%BE%E0%AA%A4%E0%AB%80-%E0%AA%AF%E0%AB%81%E0%AA%A8%E0%AA%BF%E0%AA%95%E0%AB%8B%E0%AA%A1-%E0%AA%AB%E0%AB%8B%E0%AA%A8%E0%AB%8D%E0%AA%9F-%E0%AA%B8%E0%AB%82%E0%AA%9A%E0%AB%80---List-of-Gujarati-Unicode-Fonts">collection of
Gujarati fonts&lt;/a> in this GitHub repository:
&lt;a href="https://github.com/samyakbhuta/chhapkaam">github.com/samyakbhuta/chhapkaam&lt;/a>
&lt;span class="sidenote-number">&lt;small class="sidenote">
The repo name is &lt;em>chhapkaam&lt;/em> (I
would have spelled it as &lt;em>chhaapkaam&lt;/em>) which is the Gujarati word
છાપકામ, meaning &amp;ldquo;printing&amp;rdquo;.
&lt;/small>&lt;/span>
.&lt;/p>
&lt;p>Below is my further curated list of fonts from the above list:&lt;/p>
&lt;p>&lt;a id="table--gujarati-fonts">&lt;/a>&lt;/p>
&lt;div class="table-caption">
&lt;span class="table-number">&lt;a href="#table--gujarati-fonts">Table 1&lt;/a>:&lt;/span>
Gujarati Fonts
&lt;/div>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Font Name&lt;/th>
&lt;th>&lt;em>Namaste&lt;/em>&lt;/th>
&lt;th>Category&lt;/th>
&lt;th>Homepage&lt;/th>
&lt;th>Download&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Shruti&lt;/strong>&lt;/td>
&lt;td>&lt;img src="namaste_shruti.png" alt="">&lt;/td>
&lt;td>serif&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;a href="https://www.wfonts.com/font/shruti">wfonts.com&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Mukta Vaani&lt;/strong>&lt;/td>
&lt;td>&lt;img src="namaste_mukta_vaani.png" alt="">&lt;/td>
&lt;td>serif&lt;/td>
&lt;td>&lt;a href="https://ektype.in/scripts/gujarati/mukta-vaani.html">Ek Type &amp;ndash; Mukta Vaani&lt;/a>&lt;/td>
&lt;td>&lt;a href="https://github.com/EkType/Mukta/releases">GitHub&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Lohit Gujarati&lt;/strong>&lt;/td>
&lt;td>&lt;img src="namaste_lohit.png" alt="">&lt;/td>
&lt;td>sans serif&lt;/td>
&lt;td>&lt;a href="https://pagure.io/lohit">Pagure &amp;ndash; Lohit&lt;/a>&lt;/td>
&lt;td>&lt;a href="https://releases.pagure.org/lohit/">pagure releases&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>To use these fonts, after downloading and installing them on your
system, evaluate &lt;a href="#code-snippet--gujarati-set-fontset-font">Code Snippet 1&lt;/a> above with the
correct &amp;ldquo;FONT NAME&amp;rdquo;. For example, to set the Gujarati text to use the
&lt;em>Shruti&lt;/em> font, evaluate &lt;code class="code-inline language-emacs-lisp">&lt;span class="p">(&lt;/span>&lt;span class="nf">set-fontset-font&lt;/span> &lt;span class="s">&amp;#34;fontset-default&amp;#34;&lt;/span> &lt;span class="ss">&amp;#39;gujarati&lt;/span> &lt;span class="s">&amp;#34;Shruti&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;/code>.&lt;/p>
&lt;p>Thanks for reading (વાંચવા બદલ આભાર) 🙏!&lt;/p></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/emacs">emacs</category><category domain="https://scripter.co/series/gujarati-in-emacs">Gujarati in Emacs</category><category domain="https://scripter.co/tags/gujarati">gujarati</category><category domain="https://scripter.co/tags/font">font</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/gujarati-fonts-in-emacs/</guid><pubDate>Sun, 19 Jun 2022 01:31:00 -0400</pubDate></item><item><title>Emacs Lisp: Advice Combinators</title><link>https://scripter.co/emacs-lisp-advice-combinators/</link><description>&lt;blockquote>My diagrammatic take on summarizing all the Emacs advice combinators.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#overview-on-using-advices">Overview on using advices&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--before">&lt;span class="section-num">1&lt;/span> &lt;code>:before&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--after">&lt;span class="section-num">2&lt;/span> &lt;code>:after&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--override">&lt;span class="section-num">3&lt;/span> &lt;code>:override&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--around">&lt;span class="section-num">4&lt;/span> &lt;code>:around&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--before-while">&lt;span class="section-num">5&lt;/span> &lt;code>:before-while&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--before-until">&lt;span class="section-num">6&lt;/span> &lt;code>:before-until&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--after-while">&lt;span class="section-num">7&lt;/span> &lt;code>:after-while&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--after-until">&lt;span class="section-num">8&lt;/span> &lt;code>:after-until&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--filter-args">&lt;span class="section-num">9&lt;/span> &lt;code>:filter-args&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--filter-return">&lt;span class="section-num">10&lt;/span> &lt;code>:filter-return&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;li>&lt;a href="#advice-combinators--references">References&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>If you have read some of my earlier posts, you would know that I
really enjoy using the Emacs Advice system 😃.&lt;/p>
&lt;p>The &amp;ldquo;advice&amp;rdquo; feature lets you add to the existing definition of a
function, by &lt;em>advising the function&lt;/em>. This is a cleaner method than
redefining the whole function, because it&amp;rsquo;s easier to debug and if you
don&amp;rsquo;t need it, you can just &lt;em>remove&lt;/em> the advice.&lt;/p>
&lt;div class="note">
&lt;p>You can jump to the &lt;a href="#advice-combinators--references">References section&lt;/a> below if you need to look at
the related sections in the Emacs Lisp Manual.&lt;/p>
&lt;/div>
&lt;h2 id="overview-on-using-advices">Overview on using advices&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#overview-on-using-advices">#&lt;/a>&lt;/h2>
&lt;p>I do not plan to write a tutorial on how to write advices in
Emacs-Lisp, but here&amp;rsquo;s a 3-second primer:&lt;/p>
&lt;dl>
&lt;dt>To add an advice&lt;/dt>
&lt;dd>&lt;code class="code-inline language-emacs-lisp">&lt;span class="p">(&lt;/span>&lt;span class="nv">advice-add&lt;/span> &lt;span class="ss">&amp;#39;original-fn&lt;/span> &lt;span class="nv">&amp;lt;combinator&amp;gt;&lt;/span> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">advising-fn&lt;/span>&lt;span class="p">)&lt;/span>&lt;/code>&lt;/dd>
&lt;dt>To remove an advice&lt;/dt>
&lt;dd>&lt;code class="code-inline language-emacs-lisp">&lt;span class="p">(&lt;/span>&lt;span class="nv">advice-remove&lt;/span> &lt;span class="ss">&amp;#39;original-fn&lt;/span> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">advising-fn&lt;/span>&lt;span class="p">)&lt;/span>&lt;/code>&lt;/dd>
&lt;/dl>
&lt;p>This article attempts to briefly describe different ways of advising a
function, using 10 different &lt;em>combinators&lt;/em>. If you have never used
advices in your Emacs config, don&amp;rsquo;t worry. I am hopeful that the
diagrams in this post and the examples linked for some of the
combinators in the Summary section makes this concept a bit easier to
assimilate.&lt;/p>
&lt;dl>
&lt;dt>Diagram Legend&lt;/dt>
&lt;dd>&lt;ul>
&lt;li>Initial black circle: Original Fn input arguments&lt;/li>
&lt;li>Yellow box: Original Fn&lt;/li>
&lt;li>Gray box: Advising Fn&lt;/li>
&lt;li>Black circle inside a white circle: Return values&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;/dl>
&lt;h2 id="advice-combinators--before">&lt;span class="section-num">1&lt;/span> &lt;code>:before&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--before">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-before">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/before.svg" alt="Figure 1: :before advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>&lt;strong>:before&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--after">&lt;span class="section-num">2&lt;/span> &lt;code>:after&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--after">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-after">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/after.svg" alt="Figure 2: :after advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 2: &lt;/span>&lt;strong>:after&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--override">&lt;span class="section-num">3&lt;/span> &lt;code>:override&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--override">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-override">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/override.svg" alt="Figure 3: :override advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 3: &lt;/span>&lt;strong>:override&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--around">&lt;span class="section-num">4&lt;/span> &lt;code>:around&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--around">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-around">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/around.svg" alt="Figure 4: :around advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 4: &lt;/span>&lt;strong>:around&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--before-while">&lt;span class="section-num">5&lt;/span> &lt;code>:before-while&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--before-while">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-before-while">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/before-while.svg" alt="Figure 5: :before-while advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 5: &lt;/span>&lt;strong>:before-while&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--before-until">&lt;span class="section-num">6&lt;/span> &lt;code>:before-until&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--before-until">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-before-until">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/before-until.svg" alt="Figure 6: :before-until advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 6: &lt;/span>&lt;strong>:before-until&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--after-while">&lt;span class="section-num">7&lt;/span> &lt;code>:after-while&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--after-while">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-after-while">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/after-while.svg" alt="Figure 7: :after-while advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 7: &lt;/span>&lt;strong>:after-while&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--after-until">&lt;span class="section-num">8&lt;/span> &lt;code>:after-until&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--after-until">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-after-until">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/after-until.svg" alt="Figure 8: :after-until advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 8: &lt;/span>&lt;strong>:after-until&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--filter-args">&lt;span class="section-num">9&lt;/span> &lt;code>:filter-args&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--filter-args">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-filter-args">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/filter-args.svg" alt="Figure 9: :filter-args advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 9: &lt;/span>&lt;strong>:filter-args&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="advice-combinators--filter-return">&lt;span class="section-num">10&lt;/span> &lt;code>:filter-return&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--filter-return">#&lt;/a>&lt;/h2>
&lt;p>&lt;a id="figure--advice-combinators-filter-return">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/emacs-lisp-advice-combinators/filter-return.svg" alt="Figure 10: :filter-return advice"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 10: &lt;/span>&lt;strong>:filter-return&lt;/strong> advice
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="summary">Summary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#summary">#&lt;/a>&lt;/h2>
&lt;p>Here&amp;rsquo;s a concise summary of what each advice combinator does. For
brevity, the &lt;em>advising function&lt;/em> is called &lt;em>A&lt;/em> and the &lt;em>original
function&lt;/em> is called &lt;em>O&lt;/em>.&lt;/p>
&lt;div class="note">
&lt;p>Once you click on any of the example posts, search for &lt;code>advice-add&lt;/code> on
that page to find the code example.&lt;/p>
&lt;/div>
&lt;p>&lt;a id="table--advice-combinator-summary">&lt;/a>&lt;/p>
&lt;div class="table-caption">
&lt;span class="table-number">&lt;a href="#table--advice-combinator-summary">Table 1&lt;/a>:&lt;/span>
Summary of what each advice combinator means
&lt;/div>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Combinator&lt;/th>
&lt;th>Description&lt;/th>
&lt;th>Example&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>:before&lt;/code>&lt;/td>
&lt;td>&lt;em>A&lt;/em> is called before &lt;em>O&lt;/em>. &lt;em>O&lt;/em> args and return values are not modified.&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:after&lt;/code>&lt;/td>
&lt;td>&lt;em>A&lt;/em> is called after &lt;em>O&lt;/em>. &lt;em>O&lt;/em> args and return values are not modified.&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:override&lt;/code>&lt;/td>
&lt;td>&lt;em>A&lt;/em> is called in lieu of &lt;em>O&lt;/em>. &lt;em>A&lt;/em> gets the same args as &lt;em>O&lt;/em>.&lt;/td>
&lt;td>&lt;a href="https://scripter.co/zero-html-validation-errors/">Zero HTML Validation Errors!&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:around&lt;/code>&lt;/td>
&lt;td>&lt;em>A&lt;/em> is called in lieu of &lt;em>O&lt;/em>. &lt;em>A&lt;/em> gets &lt;em>O&lt;/em> fn + &lt;em>O&lt;/em> args as args.&lt;/td>
&lt;td>&lt;a href="https://scripter.co/using-emacs-advice-to-silence-messages-from-functions/">Using Emacs advice to silence messages from functions&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:before-while&lt;/code>&lt;/td>
&lt;td>&lt;em>A&lt;/em> is called first. If it returns non-nil, &lt;em>O&lt;/em> is called.&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:before-until&lt;/code>&lt;/td>
&lt;td>&lt;em>A&lt;/em> is called first. If it returns nil, &lt;em>O&lt;/em> is called.&lt;/td>
&lt;td>&lt;a href="https://scripter.co/org-show-only-post-subtree-headings/">Org: Show only Post subtree headings&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:after-while&lt;/code>&lt;/td>
&lt;td>&lt;em>O&lt;/em> is called first. If it returns non-nil, &lt;em>A&lt;/em> is called.&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:after-until&lt;/code>&lt;/td>
&lt;td>&lt;em>O&lt;/em> is called first. If it returns nil, &lt;em>A&lt;/em> is called.&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:filter-args&lt;/code>&lt;/td>
&lt;td>&lt;em>A&lt;/em> is called first. &lt;em>O&lt;/em> is called next with return value from &lt;em>A&lt;/em> as input.&lt;/td>
&lt;td>&lt;a href="https://scripter.co/narrowing-the-author-column-in-magit/">Narrowing the Author column in Magit&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>:filter-return&lt;/code>&lt;/td>
&lt;td>&lt;em>O&lt;/em> is called first. &lt;em>A&lt;/em> is called next with return value from &lt;em>O&lt;/em> as input.&lt;/td>
&lt;td>&lt;a href="https://scripter.co/zero-html-validation-errors/">Zero HTML Validation Errors!&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>If you have any feedback on how these diagrams can be made easier to
understand, please let me know.&lt;/p>
&lt;h2 id="advice-combinators--references">References&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#advice-combinators--references">#&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Advising-Functions.html" title="Emacs Lisp: (info &amp;quot;(elisp) Advising Functions&amp;quot;)">Elisp Info: Advising Functions&lt;/a>
&lt;ul>
&lt;li>&lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Advice-Combinators.html" title="Emacs Lisp: (info &amp;quot;(elisp) Advice Combinators&amp;quot;)">Elisp Info: Advice Combinators&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/emacs">emacs</category><category domain="https://scripter.co/categories/elisp">elisp</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><category domain="https://scripter.co/tags/advice">advice</category><category domain="https://scripter.co/tags/plantuml">plantuml</category><guid>https://scripter.co/emacs-lisp-advice-combinators/</guid><pubDate>Fri, 17 Jun 2022 21:30:00 -0400</pubDate></item><item><title>Org: Show only Post subtree headings</title><link>https://scripter.co/org-show-only-post-subtree-headings/</link><description>&lt;blockquote>How to define a custom &lt;code>org-global-cycle&lt;/code>-like command that collapses
only the Org subtrees with specific properties.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#org-global-cycle">Org Global Cycle&lt;/a>&lt;/li>
&lt;li>&lt;a href="#skeleton-of-only-post-headings">Skeleton of only Post headings&lt;/a>&lt;/li>
&lt;li>&lt;a href="#the-collapse-all-posts-function">The &amp;ldquo;Collapse All Posts&amp;rdquo; function&lt;/a>&lt;/li>
&lt;li>&lt;a href="#binding-with-c-u-c-c-tab">Binding with &lt;code>C-u C-c TAB&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#result">Result&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I start this post by introducing what the Org mode global cycling
command does, what kind of subtree folding I actually need, and then
share the solution with code snippets.&lt;/p>
&lt;h2 id="org-global-cycle">Org Global Cycle&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#org-global-cycle">#&lt;/a>&lt;/h2>
&lt;p>Org mode has a built-in &lt;code>org-global-cycle&lt;/code> command that you might be
familiar with. It&amp;rsquo;s bound by default to the &lt;code>S-TAB&lt;/code> key. Each time
this command is called, the Org buffer will cycle through these
states:&lt;/p>
&lt;ol>
&lt;li>Overview: Show only the Level 1 headings and collapse everything
underneath.&lt;/li>
&lt;li>Contents: Show only the Org headings and collapse all the content.&lt;/li>
&lt;li>Show All: Expand all the headings and show their contents too.&lt;/li>
&lt;/ol>
&lt;p>If a numeric prefix &lt;em>N&lt;/em> is used with this command, it will show only
the Org headings up to Level &lt;em>N&lt;/em>. For example, &lt;code>C-2 S-TAB&lt;/code> will show
only the headings up to Level 2.&lt;/p>
&lt;p>This is a really helpful command, but I needed something different ..&lt;/p>
&lt;h2 id="skeleton-of-only-post-headings">Skeleton of only Post headings&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#skeleton-of-only-post-headings">#&lt;/a>&lt;/h2>
&lt;p>I maintain most of this website&amp;rsquo;s content in a single Org file. I have
dozens of blog posts organized in Org subtrees, which I further
organize under &amp;ldquo;category&amp;rdquo; headings .. It kind of looks like the below
mock-up:
&lt;span class="sidenote-number">&lt;small class="sidenote">
It&amp;rsquo;s amazing how many features PlantUML has. If you are interested in
creating diagrams like these, check out the &lt;a href="https://plantuml.com/salt">PlantUML Salt&lt;/a> syntax.
&lt;/small>&lt;/span>&lt;/p>
&lt;p>&lt;a id="figure--post-subtrees-collapsed-mockup">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/org-show-only-post-subtree-headings/post-subtrees.svg" alt="Figure 1: Post Subtrees at arbitrary heading levels"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>Post Subtrees at arbitrary heading levels
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;p>As we can see,&lt;/p>
&lt;ul>
&lt;li>All the post subtrees are not at Level 1 headings.&lt;/li>
&lt;li>They are also not at a fixed Level &lt;em>N&lt;/em>.&lt;/li>
&lt;li>The heading level of the post depends on how many parent categories
that post has (and that will also change over time).&lt;/li>
&lt;/ul>
&lt;p>I needed to basically show everything leading up to a post subtree
heading, and then collapse all the content under that post; even the
sub-headings.&lt;/p>
&lt;h2 id="the-collapse-all-posts-function">The &amp;ldquo;Collapse All Posts&amp;rdquo; function&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#the-collapse-all-posts-function">#&lt;/a>&lt;/h2>
&lt;p>The &lt;code>modi/org-hugo-collapse-all-posts&lt;/code> function defined below meets
the above requirement:&lt;/p>
&lt;ol>
&lt;li>It first widens the whole buffer and expands all the headings.&lt;/li>
&lt;li>Then it loops through all the headings and collapses all the &lt;em>post
subtrees&lt;/em> i.e. all the subtrees that have the &lt;code>EXPORT_FILE_NAME&lt;/code>
property set. This is where I use the &lt;a href="https://scripter.co/looping-through-org-mode-headings/">&lt;code>org-map-entries&lt;/code>&lt;/a> magic.&lt;/li>
&lt;li>Finally it looks for Org headings that begin with &amp;ldquo;Footnotes&amp;rdquo; or
&amp;ldquo;COMMENT&amp;rdquo; and collapses them as well.&lt;/li>
&lt;/ol>
&lt;p>I am using the development version of Org mode (version 9.6, yet to be
released as of &lt;span class="timestamp-wrapper">&lt;span class="timestamp">&amp;lt;2022-06-15 Wed&amp;gt;&lt;/span>&lt;/span>) which has the new &lt;code>org-fold&lt;/code>
library. This library obsoletes the use of &lt;code>outline.el&lt;/code> library and
other &lt;em>code-folding&lt;/em> related functions in Org mode. So &lt;code>cl-flet&lt;/code> is
used to create function symbol aliases that use the &lt;code>org-fold-*&lt;/code>
functions if available, otherwise they fall back to the legacy
functions.&lt;/p>
&lt;p>&lt;a id="code-snippet--collapse-all-posts-fn">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="hl">&lt;span class="lnt">19
&lt;/span>&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="hl">&lt;span class="lnt">23
&lt;/span>&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nb">defun&lt;/span> &lt;span class="nv">modi/org-hugo-collapse-all-posts&lt;/span> &lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;Collapse all post subtrees in the current buffer.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">Also collapse the Footnotes subtree and COMMENT subtrees if
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">present.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">A post subtree is one that has the EXPORT_FILE_NAME property
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">set.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">interactive&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">cl-flet&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nv">show-all&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">fboundp&lt;/span> &lt;span class="ss">&amp;#39;org-fold-show-all&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">org-fold-show-all&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">org-show-all&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">hide-subtree&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">fboundp&lt;/span> &lt;span class="ss">&amp;#39;org-fold-hide-subtree&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">org-fold-hide-subtree&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">outline-hide-subtree&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nf">widen&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">show-all&lt;/span> &lt;span class="o">&amp;#39;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">headings&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">;; Collapse all the post subtrees (ones with EXPORT_FILE_NAME&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">;; property set).&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">org-map-entries&lt;/span> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">hide-subtree&lt;/span> &lt;span class="s">&amp;#34;EXPORT_FILE_NAME&amp;lt;&amp;gt;\&amp;#34;\&amp;#34;&amp;#34;&lt;/span> &lt;span class="ss">&amp;#39;file&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">;; Also hide Footnotes and comments.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">save-excursion&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nf">goto-char&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">point-min&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">re-search-forward&lt;/span> &lt;span class="s">&amp;#34;^\\(\\* Footnotes\\|\\*+ COMMENT\\)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="no">nil&lt;/span> &lt;span class="nb">:noerror&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">hide-subtree&lt;/span>&lt;span class="p">)))))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--collapse-all-posts-fn">Code Snippet 1&lt;/a>:&lt;/span>
Function that collapses all the post subtrees in the current buffer
&lt;/div>
&lt;h2 id="binding-with-c-u-c-c-tab">Binding with &lt;code>C-u C-c TAB&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#binding-with-c-u-c-c-tab">#&lt;/a>&lt;/h2>
&lt;p>The function is ready, but let&amp;rsquo;s now add a bit of convenience to it.&lt;/p>
&lt;p>If a point is under a subtree, &lt;code>C-c TAB&lt;/code> will collapse that subtree
while showing only Level 1 headings, and if a numeric prefix is used,
it will show only those many levels of headings. I decided to bind the
above function to &lt;code>C-u C-c TAB&lt;/code> because,&lt;/p>
&lt;ol>
&lt;li>The behavior of &lt;code>modi/org-hugo-collapse-all-posts&lt;/code> falls in the
same category as that of &lt;code>C-c TAB&lt;/code>.&lt;/li>
&lt;li>The &lt;code>C-u C-c ..&lt;/code> binding rolls off the fingers pretty
nicely 😃.&lt;/li>
&lt;/ol>
&lt;p>This &lt;em>binding&lt;/em> is achieved using one of my favorite Emacs features
.. the &lt;strong>advice&lt;/strong> system. The &lt;code>:before-until&lt;/code> &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Advice-Combinators.html" title="Emacs Lisp: (info &amp;quot;(elisp) Advice Combinators&amp;quot;)">Advice Combinator&lt;/a> is used
here, which means that if the &lt;em>advising&lt;/em> function (below) returns a
&lt;em>nil&lt;/em>, the &lt;em>advised&lt;/em> or the original function &lt;code>org-ctrl-c-tab&lt;/code> is not
called.&lt;/p>
&lt;p>The &lt;em>advising&lt;/em> function below detects if the &lt;code>C-u&lt;/code> prefix argument is
used. If it is, the &lt;code>modi/org-hugo-collapse-all-posts&lt;/code> function is
called, otherwise the original &lt;code>org-ctrl-c-tab&lt;/code> function is called.&lt;/p>
&lt;p>&lt;a id="code-snippet--collapse-all-posts-binding">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="hl">&lt;span class="lnt">4
&lt;/span>&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nb">defun&lt;/span> &lt;span class="nv">modi/org-ctrl-c-tab-advice&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kp">&amp;amp;rest&lt;/span> &lt;span class="nv">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;Run &lt;/span>&lt;span class="ss">`modi/org-hugo-collapse-all-posts&amp;#39;&lt;/span>&lt;span class="s"> when
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">doing \\[universal-argument] \\[org-ctrl-c-tab].&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line hl">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">let&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nv">do-not-run-orig-fn&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">equal&lt;/span> &lt;span class="o">&amp;#39;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nv">current-prefix-arg&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">when&lt;/span> &lt;span class="nv">do-not-run-orig-fn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">modi/org-hugo-collapse-all-posts&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">do-not-run-orig-fn&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nv">advice-add&lt;/span> &lt;span class="ss">&amp;#39;org-ctrl-c-tab&lt;/span> &lt;span class="nb">:before-until&lt;/span> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">modi/org-ctrl-c-tab-advice&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--collapse-all-posts-binding">Code Snippet 2&lt;/a>:&lt;/span>
Bind &lt;code>C-u C-c TAB&lt;/code> to call &lt;code>modi/org-hugo-collapse-all-posts&lt;/code>
&lt;/div>
&lt;h2 id="result">Result&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#result">#&lt;/a>&lt;/h2>
&lt;p>After evaluating the above two snippets, when I do &lt;code>C-u C-c TAB&lt;/code> in
my &amp;ldquo;blog posts&amp;rdquo; Org buffer, I see this:&lt;/p>
&lt;p>&lt;a id="figure--post-subtrees-collapsed">&lt;/a>&lt;/p>
&lt;figure>
&lt;a href="post-subtrees-collapsed.png">
&lt;img src="https://scripter.co/org-show-only-post-subtree-headings/post-subtrees-collapsed.png" alt="Figure 2: My &amp;ldquo;blog posts&amp;rdquo; Org buffer showing only the Post subtree headings"/> &lt;/a>&lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 2: &lt;/span>My &amp;ldquo;blog posts&amp;rdquo; Org buffer showing only the Post subtree headings
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;p>It matches &lt;a href="#figure--post-subtrees-collapsed-mockup">that earlier mockup&lt;/a> &amp;mdash; Mission accomplished! 💯&lt;/p></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/emacs">emacs</category><category domain="https://scripter.co/categories/org">org</category><category domain="https://scripter.co/categories/elisp">elisp</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><category domain="https://scripter.co/tags/subtree">subtree</category><category domain="https://scripter.co/tags/looping">looping</category><category domain="https://scripter.co/tags/advice">advice</category><guid>https://scripter.co/org-show-only-post-subtree-headings/</guid><pubDate>Thu, 16 Jun 2022 00:21:00 -0400</pubDate></item><item><title>Firefox: Always open a New Tab after Current</title><link>https://scripter.co/firefox-always-open-a-new-tab-after-current/</link><description>&lt;blockquote>The default behavior of Firefox opening new tabs from links had been
troubling me for a while .. it was time I tried to do something about
it.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#the-issue">The Issue&lt;/a>&lt;/li>
&lt;li>&lt;a href="#expected-behavior">Expected Behavior&lt;/a>&lt;/li>
&lt;li>&lt;a href="#false-leads">False Leads&lt;/a>&lt;/li>
&lt;li>&lt;a href="#firefox-bug-1485683">Firefox Bug # 1485683&lt;/a>&lt;/li>
&lt;li>&lt;a href="#solution">Solution&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>This was one of the those little things in software that bug me, but I
don&amp;rsquo;t start looking for a solution right-away because of reasons like
&amp;ldquo;I am too busy with something else&amp;rdquo;, or &amp;ldquo;The bug is not so bad.. let
me see if I can live with it&amp;rdquo;.&lt;/p>
&lt;p>But no .. this particular issue with how Firefox dealt with opening
new tabs from links was now getting on my nerves.&lt;/p>
&lt;h2 id="the-issue">The Issue&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#the-issue">#&lt;/a>&lt;/h2>
&lt;p>While visiting a page, I typically middle-click on links so that they
open in new tabs .. and I do that a lot! And this is the order of
where the new tabs would open:&lt;/p>
&lt;p>&lt;a id="figure--firefox-new-tabs-unexpected">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/firefox-always-open-a-new-tab-after-current/unexpected.svg" alt="Figure 1: Unexpected order of opening of new tabs opened after each middle-click"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 1: &lt;/span>Unexpected order of opening of new tabs opened after each middle-click
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;div class="org-center">
&lt;p>In my view, this behavior is completely wrong!&lt;/p>
&lt;/div>
&lt;p>As you can see above, if I already have a bunch of links open (see the
last row where &amp;ldquo;Tab 1&amp;rdquo;, &amp;ldquo;Tab 2&amp;rdquo; and &amp;ldquo;Tab 3&amp;rdquo; are already open), the
last opened tab will land up between those tabs and a sea of other
previously opened tabs! When you have over a dozen of tabs already
open, hunting for the &lt;em>last opened tabs&lt;/em> is really annoying.&lt;/p>
&lt;h2 id="expected-behavior">Expected Behavior&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#expected-behavior">#&lt;/a>&lt;/h2>
&lt;p>I would instead expect for each middle-click to open the new tab
&lt;strong>immediately to the right&lt;/strong> of the current tab. Below diagram shows it
very clearly that this behavior would save me the exercise of hunting
for that new tab.&lt;/p>
&lt;p>&lt;a id="figure--firefox-new-tabs-expected">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/firefox-always-open-a-new-tab-after-current/expected.svg" alt="Figure 2: Expected order of opening of new tabs opened after each middle-click"/> &lt;figcaption>
&lt;p>
&lt;span class="figure-number">Figure 2: &lt;/span>&lt;strong>Expected&lt;/strong> order of opening of new tabs opened after each middle-click
&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="false-leads">False Leads&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#false-leads">#&lt;/a>&lt;/h2>
&lt;p>So I searched online for &amp;ldquo;firefox new tab location&amp;rdquo; and similar search
terms, and found &lt;em>Q&amp;amp;A&lt;/em> on Mozilla Support like &lt;a href="https://support.mozilla.org/en-US/questions/1229062#answer-1141742">How do I make tabs open
on the right by default?&lt;/a> and &lt;a href="https://support.mozilla.org/en-US/questions/1295586#answer-1334375">New tabs ALWAYS open to the right of
the last open tab, not to the right of current tab&lt;/a>.&lt;/p>
&lt;p>The &amp;ldquo;Chosen solutions&amp;rdquo; for both of these issues suggested setting
&lt;code>browser.tabs.insertAfterCurrent = true&lt;/code> and
&lt;code>browser.tabs.insertRelatedAfterCurrent = true&lt;/code> (which is the default)
in the Firefox &lt;strong>about:config&lt;/strong>.&lt;/p>
&lt;div class="verse">
&lt;p>        But that didn&amp;rsquo;t work!!&lt;br />&lt;/p>
&lt;/div>
&lt;p>Setting &lt;code>browser.tabs.insertAfterCurrent = true&lt;/code> affected the &amp;ldquo;new tab
opening&amp;rdquo; behavior when opening only blank new tabs using &lt;code>Ctrl+T&lt;/code> or
hitting the &amp;ldquo;New Tab&amp;rdquo; button.
&lt;mark>This setting had &lt;strong>nothing&lt;/strong> to do with the location of new tabs opened
by the &amp;ldquo;Open Link in New Tab&amp;rdquo; or &amp;ldquo;middle-click on link&amp;rdquo; action.&lt;/mark>&lt;/p>
&lt;h2 id="firefox-bug-1485683">Firefox Bug # 1485683&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#firefox-bug-1485683">#&lt;/a>&lt;/h2>
&lt;p>After scouring through many search results, I landed up on &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1485683">Firefox
Bug # 1485683: browser.tab.insertaftercurrent order&lt;/a> which reported
the exact same issue I was seeing 🎉.&lt;/p>
&lt;p>So I replied to that bug thread confirming that I still saw that issue
on Firefox 101.0.1 .. &lt;strong>4 years later&lt;/strong> .. just in case that motivates
someone to still fix it.&lt;/p>
&lt;p>But my issue isn&amp;rsquo;t fixed yet.&lt;/p>
&lt;h2 id="solution">Solution&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#solution">#&lt;/a>&lt;/h2>
&lt;p>Finally, the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/open-tabs-next-to-current/">Open Tabs Next to Current&lt;/a> (&lt;a href="https://github.com/sblask/webextension-open-tabs-next-to-current">repo&lt;/a>) Firefox Add-on saved the
day!&lt;/p>
&lt;p>The introduction of this add-on incorrectly states this:&lt;/p>
&lt;blockquote>
&lt;p>After the addition of the &lt;code>browser.tabs.insertAfterCurrent&lt;/code> setting in
&lt;strong>about:config&lt;/strong> you do not need this extension anymore.&lt;/p>
&lt;/blockquote>
&lt;p>But I still tried installing it to see .. just in case
.. 🤞 ..&lt;/p>
&lt;div class="verse">
&lt;p>And it worked!!&lt;br />&lt;/p>
&lt;/div>
&lt;p>I could finally get new tabs to open in the order as shown in the
&lt;a href="#figure--firefox-new-tabs-expected">Expected Tab Order figure&lt;/a> above 😂.&lt;/p>
&lt;p>Thank you Sebastian Blask for &lt;strong>Open Tabs Next to Current&lt;/strong>!&lt;/p></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/web">web</category><category domain="https://scripter.co/tags/firefox">firefox</category><category domain="https://scripter.co/tags/tab">tab</category><category domain="https://scripter.co/tags/add-on">add-on</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/firefox-always-open-a-new-tab-after-current/</guid><pubDate>Wed, 15 Jun 2022 02:08:00 -0400</pubDate></item><item><title>Saving Python pip dependencies</title><link>https://scripter.co/saving-python-pip-dependencies/</link><description>&lt;blockquote>How I generated the Python dependencies file &lt;code>requirements.txt&lt;/code> so
that Netlify can install and run the HTML5 Validator before deploying
this site.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#create-a-virtual-environment">&lt;span class="section-num">1&lt;/span> Create a &lt;em>virtual environment&lt;/em>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#activate-the-virtual-environment">&lt;span class="section-num">2&lt;/span> Activate the &lt;em>virtual environment&lt;/em>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#install-the-packages-using-pip">&lt;span class="section-num">3&lt;/span> Install the packages using &lt;code>pip&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#generate-requirements-dot-txt">&lt;span class="section-num">4&lt;/span> Generate &lt;code>requirements.txt&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#netlify-deployment">Netlify deployment&lt;/a>&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>Recently I learned about the Python tool &lt;code>html5validator&lt;/code> tool and
used it to run HTML5 validation on local HTML files. You can read more
about that in the &lt;a href="https://scripter.co/offline-html5-validator/">Offline HTML5 Validator&lt;/a> post.&lt;/p>
&lt;p>But then I wondered .. &amp;ldquo;Wouldn&amp;rsquo;t it be awesome if I run this
validation step &lt;strong>each time&lt;/strong> after Hugo generates this site on &lt;a href="https://netlify.com">Netlify&lt;/a>,
but &lt;span class="underline">before&lt;/span> it gets deployed?&amp;rdquo;. That curiosity led me to &lt;a href="https://docs.netlify.com/configure-builds/manage-dependencies/#python-dependencies">Netlify&amp;rsquo;s
documentation on Python dependencies&lt;/a>, and I learned that Netlify will
run &lt;code>pip install&lt;/code> to install the dependencies in &lt;code>requirements.txt&lt;/code>
present in the repository&amp;rsquo;s base directory.&lt;/p>
&lt;p>&lt;span class="org-target" id="netlify-pip-freeze">&lt;/span> I followed the &lt;code>pip freeze &amp;gt; requirements.txt&lt;/code>
step in that documentation but that ended up listing &lt;strong>all&lt;/strong> my &lt;code>pip&lt;/code>
installed packages in that file! I needed the &lt;code>requirements.txt&lt;/code> to
include the dependencies only for &lt;code>html5validator&lt;/code>. This post was born
in my quest to achieve that.&lt;/p>
&lt;p>The solution to this problem was to first create a Python &lt;em>virtual
environment&lt;/em> in my site directory, and &lt;em>then&lt;/em> do all the &lt;code>pip&lt;/code>
operations in there. I learned about this &lt;em>virtual environment&lt;/em> step
from &lt;a href="https://medium.com/python-pandemonium/better-python-dependency-and-package-management-b5d8ea29dff1">this Python Pandemonium post&lt;/a>.&lt;/p>
&lt;p>The &lt;a href="https://docs.python.org/3/library/venv.html">&lt;strong>venv&lt;/strong> documentation&lt;/a> has a lot of details &amp;mdash; I&amp;rsquo;ll just list the
key steps in this post.&lt;/p>
&lt;h2 id="create-a-virtual-environment">&lt;span class="section-num">1&lt;/span> Create a &lt;em>virtual environment&lt;/em>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#create-a-virtual-environment">#&lt;/a>&lt;/h2>
&lt;p>&lt;em>cd&lt;/em> to your site directory and run the below command create a
virtualenv directory named &lt;code>pyenv&lt;/code> in there.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">python3 -m venv pyenv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="note">
&lt;p>As we are creating this &lt;em>virtual environment&lt;/em> just for the sake of
creating a &lt;code>requirements.txt&lt;/code>, we don&amp;rsquo;t need to commit this
directory to &lt;em>git&lt;/em>.&lt;/p>
&lt;/div>
&lt;h2 id="activate-the-virtual-environment">&lt;span class="section-num">2&lt;/span> Activate the &lt;em>virtual environment&lt;/em>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#activate-the-virtual-environment">#&lt;/a>&lt;/h2>
&lt;p>The virtualenv directory &lt;code>pyenv/bin/&lt;/code> will have multiple flavors of
shell scripts to activate your virtualenv. I am using
&lt;code>activate.csh&lt;/code> here as my shell is &lt;em>tcsh&lt;/em> 🙄.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> pyenv/bin/activate.csh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Just to emphasize, it is important to use the &lt;strong>&lt;code>source&lt;/code>&lt;/strong> command here,
and not just call the shell script directly.&lt;/p>
&lt;p>Once you activate this virtualenv, you should see something like
&lt;em>[pyenv]&lt;/em> in the shell prompt.&lt;/p>
&lt;h2 id="install-the-packages-using-pip">&lt;span class="section-num">3&lt;/span> Install the packages using &lt;code>pip&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#install-the-packages-using-pip">#&lt;/a>&lt;/h2>
&lt;p>Install all the project-specific packages. In this case it was just
this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pip install html5validator
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The package (and its dependencies) will be installed in
&lt;code>pyenv/lib/python3.7/site-packages/&lt;/code>. Here, the &lt;code>python3.7/&lt;/code>
sub-directory name will match the version of Python you have
installed.&lt;/p>
&lt;h2 id="generate-requirements-dot-txt">&lt;span class="section-num">4&lt;/span> Generate &lt;code>requirements.txt&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#generate-requirements-dot-txt">#&lt;/a>&lt;/h2>
&lt;p>Finally, we run the &lt;code>pip freeze&lt;/code> command &lt;a href="#netlify-pip-freeze">mentioned&lt;/a> in Netlify docs,
but with the &lt;strong>&lt;code>--local&lt;/code>&lt;/strong> switch:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">pip freeze --local &amp;gt; requirements.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="note">
&lt;p>The &lt;code>--local&lt;/code> option ensures that globally-installed packages are not
listed even if the virtualenv has global access.&lt;/p>
&lt;/div>
&lt;p>Here&amp;rsquo;s the &lt;code>requirements.txt&lt;/code> created by that command:&lt;/p>
&lt;p>&lt;a id="code-snippet--netlify-requirements.txt">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">html5validator==0.4.2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PyYAML==6.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--netlify-requirements.txt">Code Snippet 1&lt;/a>:&lt;/span>
The &lt;code>requirements.txt&lt;/code> for Netlify
&lt;/div>
&lt;p>Now, I could have just manually typed &lt;code>html5validator==0.4.2&lt;/code> in a
&lt;code>requirements.txt&lt;/code> and committed that, and that would work too. But
then, I wouldn&amp;rsquo;t have learned how to create a project-specific &lt;em>pip
dependency file&lt;/em> 😄.&lt;/p>
&lt;h2 id="netlify-deployment">Netlify deployment&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#netlify-deployment">#&lt;/a>&lt;/h2>
&lt;p>The &lt;code>html5validator&lt;/code> has a dependency on Java 8. Luckily the Netlify
environment already comes with that installed. So the only extra setup
needed to make this package work on Netlify was to set the
&lt;code>PYTHON_VERSION&lt;/code> environment variable to &lt;strong>3.8&lt;/strong>
&lt;span class="sidenote-number">&lt;small class="sidenote">
The version number should be one of the values listed in &lt;a href="https://github.com/netlify/build-image/blob/focal/included_software.md">Netlify&amp;rsquo;s
&lt;em>included software&lt;/em> list&lt;/a>.
&lt;/small>&lt;/span>
.&lt;/p>
&lt;h2 id="summary">Summary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#summary">#&lt;/a>&lt;/h2>
&lt;p>The numbered headings in this post already summarize the steps needed
to create a project-specific &lt;code>requirements.txt&lt;/code>.&lt;/p>
&lt;p>With these in place:&lt;/p>
&lt;div class="verse">
&lt;p>✅ &lt;code>requirements.txt&lt;/code> committed in site directory root&lt;br />
✅ Netlify environment variable &lt;code>PYTHON_VERSION&lt;/code> set to 3.8&lt;br />&lt;/p>
&lt;/div>
&lt;p>my Netlify deployment script now does HTML5 Validation along with few
other checks before this site gets deployed 🎉.&lt;/p>
&lt;p>Here&amp;rsquo;s the &lt;a href="https://gitlab.com/kaushalmodi/kaushalmodi.gitlab.io/-/blob/master/build.sh">full &lt;code>build.sh&lt;/code> script&lt;/a>.&lt;/p></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/web">web</category><category domain="https://scripter.co/series/html5-validator">HTML5 Validator</category><category domain="https://scripter.co/tags/html">html</category><category domain="https://scripter.co/tags/validator">validator</category><category domain="https://scripter.co/tags/python">python</category><category domain="https://scripter.co/tags/pip">pip</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><category domain="https://scripter.co/tags/netlify">netlify</category><guid>https://scripter.co/saving-python-pip-dependencies/</guid><pubDate>Tue, 14 Jun 2022 00:38:00 -0400</pubDate></item><item><title>Disarming the 'tar' bomb in 10 seconds</title><link>https://scripter.co/disarming-the-tar-bomb-in-10-seconds/</link><description>&lt;blockquote>Use &lt;code>tar -caf &amp;lt;file&amp;gt; &amp;lt;dir&amp;gt;&lt;/code> to create an archive, &lt;code>tar -xf &amp;lt;file&amp;gt;&lt;/code> to
extract one, and more.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#mnemonics">Mnemonics&lt;/a>&lt;/li>
&lt;li>&lt;a href="#creating-an-archive">Creating an archive&lt;/a>&lt;/li>
&lt;li>&lt;a href="#extracting-an-archive">Extracting an archive&lt;/a>&lt;/li>
&lt;li>&lt;a href="#listing-contents-of-an-archive">Listing contents of an archive&lt;/a>&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I had come across &lt;a href="https://fosstodon.org/@garritfra/108409516362853350">this post by user &lt;em>Garrit&lt;/em>&lt;/a> on Mastodon, and that
inspired this post.&lt;/p>
&lt;div class="mf2 reply">In reply to: &lt;p>&lt;a class="u-in-reply-to h-cite" rel="in-reply-to" href="https://garrit.xyz/posts/2022-06-02-tar-commands">https://garrit.xyz/posts/2022-06-02-tar-commands&lt;/a>&lt;/p>&lt;/div>
&lt;p>A post on the Unix command &lt;a href="https://www.gnu.org/software/tar/manual/html_node/index.html" title="Emacs Lisp: (info &amp;quot;(tar) Top&amp;quot;)">&lt;code>tar&lt;/code>&lt;/a> cannot leave out the obligatory &lt;em>xkcd
&lt;strong>tar&lt;/strong>&lt;/em> comic 😄, so here it is:&lt;/p>
&lt;p>&lt;a id="figure--xkcd-1168">&lt;/a>&lt;/p>
&lt;figure>
&lt;img src="https://scripter.co/disarming-the-tar-bomb-in-10-seconds/xkcd-1168.png" title="I don't know what's worse--the fact that after 15 years of using tar I still can't keep the flags straight, or that after 15 years of technological advancement I'm still mucking with tar flags that were 15 years old when I started."/> &lt;figcaption>
&lt;p>
&lt;a href="https://xkcd.com/1168/">
xkcd.com
&lt;/a>&lt;/p>
&lt;/figcaption>&lt;/figure>
&lt;h2 id="mnemonics">Mnemonics&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#mnemonics">#&lt;/a>&lt;/h2>
&lt;p>These few mnemonics help me remember the basic and my most frequently
used &lt;code>tar&lt;/code> options:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>c&lt;/strong> to (c)reate&lt;/li>
&lt;li>&lt;strong>a&lt;/strong> to (a)uto compress the archive based on the file name&lt;/li>
&lt;li>&lt;strong>x&lt;/strong> to e(x)tract&lt;/li>
&lt;li>&lt;strong>f&lt;/strong> for the archive (f)ile name we are dealing with (whether
creating, listing or extracting an archive)&lt;/li>
&lt;/ul>
&lt;p>.. and a few not so frequent options (for me):&lt;/p>
&lt;ul>
&lt;li>&lt;strong>t&lt;/strong> to lis(t) contents of an archive&lt;/li>
&lt;li>&lt;strong>v&lt;/strong> for (v)erbose output&lt;/li>
&lt;/ul>
&lt;h2 id="creating-an-archive">Creating an archive&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#creating-an-archive">#&lt;/a>&lt;/h2>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -caf &amp;lt;file&amp;gt; &amp;lt;dir&amp;gt;&lt;/strong>&lt;/p>
&lt;/div>
&lt;p>A keen user might have noticed that I am using &lt;code>tar -caf ..&lt;/code> instead
of &lt;code>tar caf ..&lt;/code> i.e. I am using a hyphen before the &lt;code>tar&lt;/code>
options. Both approaches work and they look similar, but the approach
with the hyphen is the &lt;strong>newer&lt;/strong> &lt;a href="https://www.gnu.org/software/tar/manual/html_node/Short-Options.html" title="Emacs Lisp: (info &amp;quot;(tar) Short Options&amp;quot;)">Short Option style&lt;/a> while the other is
the &lt;a href="https://www.gnu.org/software/tar/manual/html_node/Old-Options.html" title="Emacs Lisp: (info &amp;quot;(tar) Old Options&amp;quot;)">Old Option style&lt;/a>.&lt;/p>
&lt;div class="note">
&lt;p>I prefer the &lt;em>short option style&lt;/em> because .. well.. the other style is
old.. and also because the &lt;em>short option style&lt;/em> is &lt;strong>stricter&lt;/strong> e.g. the
&lt;code>-f&lt;/code> switch has to be followed by the file name.&lt;/p>
&lt;/div>
&lt;p>Whether you are creating a regular &lt;strong>.tar&lt;/strong> archive, or a compressed
archive like &lt;strong>.tar.gz&lt;/strong>, always use the auto-compresion switch
&lt;strong>-a&lt;/strong>. That relieves you from deciding &lt;em>if&lt;/em> you need that switch, or
which compression algorithm switch should be used 😄.&lt;/p>
&lt;p>The &lt;strong>-a&lt;/strong> switch makes the decision for you based on the file
extension. For example, &lt;code>tar -caf foo.tar foo/&lt;/code> will create a regular
archive, while &lt;code>tar -caf foo.tar.gz foo/&lt;/code> will create a compressed
archive using &lt;code>gzip&lt;/code>. You can read more about it in &lt;a href="https://www.gnu.org/software/tar/manual/html_node/gzip.html" title="Emacs Lisp: (info &amp;quot;(tar) gzip&amp;quot;)">Creating and
Reading Compressed Archives&lt;/a>.&lt;/p>
&lt;h2 id="extracting-an-archive">Extracting an archive&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#extracting-an-archive">#&lt;/a>&lt;/h2>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -xf &amp;lt;file&amp;gt;&lt;/strong>&lt;/p>
&lt;/div>
&lt;p>At times, it might be useful to add the verbosity switch &lt;strong>-v&lt;/strong> to this
command and do &lt;code>tar -xvf &amp;lt;file&amp;gt;&lt;/code>.&lt;/p>
&lt;div class="note">
&lt;p>Just to reiterate, the &lt;strong>-f&lt;/strong> switch must be followed by the archive
file name.&lt;/p>
&lt;/div>
&lt;h2 id="listing-contents-of-an-archive">Listing contents of an archive&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#listing-contents-of-an-archive">#&lt;/a>&lt;/h2>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -tf &amp;lt;file&amp;gt;&lt;/strong>&lt;/p>
&lt;/div>
&lt;p>Once you are done creating an archive, you might feel the need to
check if the archive contains everything you expect. Or you might want
to check what&amp;rsquo;s inside the archive before you extract it.&lt;/p>
&lt;p>This command is often paired with &lt;code>grep&lt;/code> or &lt;a href="https://github.com/BurntSushi/ripgrep">&lt;code>rg&lt;/code> (ripgrep)&lt;/a> like so:
&lt;code>tar -tf foo.tar.xz | rg 'some_file_name_in_archive'&lt;/code>.&lt;/p>
&lt;h2 id="summary">Summary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#summary">#&lt;/a>&lt;/h2>
&lt;p>If you simply glossed over the whole article, or didn&amp;rsquo;t read through
the all linked manual pages
&lt;span class="sidenote-number">&lt;small class="sidenote">
I know you didn&amp;rsquo;t 😉
&lt;/small>&lt;/span>
, just remember this &amp;mdash;&lt;/p>
&lt;div class="org-center">
&lt;p>&lt;strong>tar -caf&lt;/strong> to &lt;strong>c&lt;/strong>​reate and &lt;strong>tar -xf&lt;/strong> to e​&lt;strong>x&lt;/strong>​tract&lt;/p>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/unix">unix</category><category domain="https://scripter.co/categories/replies">replies</category><category domain="https://scripter.co/tags/tar">tar</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/disarming-the-tar-bomb-in-10-seconds/</guid><pubDate>Mon, 13 Jun 2022 01:14:00 -0400</pubDate></item><item><title>Unclutter: A better Reader View for browsers</title><link>https://scripter.co/unclutter-a-better-reader-view-for-browsers/</link><description>&lt;blockquote>Unclutter is a better looking &lt;em>Reader View&lt;/em> browser add-on available
for both Firefox and Chrome.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#why">Why?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#caveat">Caveat&lt;/a>&lt;/li>
&lt;li>&lt;a href="#closing">Closing&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I am writing this post to share this wonderful browser add-on
&lt;strong>Unclutter&lt;/strong> (available for both Firefox and Chrome) by &lt;a href="https://github.com/lindylearn">Peter Hagen&lt;/a>. I
discovered this a few days back on &lt;a href="https://news.ycombinator.com/item?id=31620466">Hacker News&lt;/a>. But given how awesome
this add-on is, I am surprised that the HN post didn&amp;rsquo;t gain any
traction.&lt;/p>
&lt;p>I&amp;rsquo;ll quickly post the important links related to this add-on, and then
briefly list few points on why I like it.&lt;/p>
&lt;div class="org-center">
&lt;p>👉 &lt;a href="https://unclutter.lindylearn.io/">&lt;strong>Unclutter Homepage&lt;/strong>&lt;/a> | &lt;a href="https://github.com/lindylearn/unclutter">&lt;strong>Source code&lt;/strong>&lt;/a> | Add-ons:
&lt;a href="https://addons.mozilla.org/en-GB/firefox/addon/lindylearn/">&lt;strong>Firefox&lt;/strong>&lt;/a>, &lt;a href="https://chrome.google.com/webstore/detail/unclutter-ad-blocker-for/ibckhpijbdmdobhhhodkceffdngnglpk">&lt;strong>Chrome&lt;/strong>&lt;/a> 👈&lt;/p>
&lt;/div>
&lt;h2 id="why">Why?&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#why">#&lt;/a>&lt;/h2>
&lt;p>I use Firefox as my primary browser. If you are too, you might be
wondering &amp;ldquo;why install this add-on when Firefox already has a &lt;a href="https://support.mozilla.org/en-US/kb/firefox-reader-view-clutter-free-web-pages">Reader
View&lt;/a>&amp;rdquo;.&lt;/p>
&lt;p>Here are my reasons:&lt;/p>
&lt;ol>
&lt;li>You do not lose the article&amp;rsquo;s &lt;strong>origin style&lt;/strong>!&lt;/li>
&lt;li>The &lt;em>Unclutter&lt;/em> author Peter Hagen was &lt;strong>super-responsive&lt;/strong> when I
&lt;a href="https://github.com/lindylearn/unclutter/issues/24">opened an issue&lt;/a> on his repo. This add-on did not work well on one
of the pages on this site, but he fixed it in less than 24 hours!&lt;/li>
&lt;li>This website shows an outline on the side, but many websites
don&amp;rsquo;t. &lt;em>Unclutter&lt;/em> adds a similar &lt;strong>outline&lt;/strong> for any page where it&amp;rsquo;s
enabled.&lt;/li>
&lt;li>&lt;em>Unclutter&lt;/em> can be &lt;strong>auto-enabled&lt;/strong> for your chosen sites. For example,
ever since I have discovered this add-on, I have auto-enabled it on
&lt;a href="https://www.masteringemacs.org">https://www.masteringemacs.org&lt;/a>.&lt;/li>
&lt;li>You can customize a &lt;strong>key-binding&lt;/strong> or shortcut to toggle this
add-on. For now, I am keeping the default binding &lt;code>Alt + C&lt;/code>.&lt;/li>
&lt;li>The &lt;strong>subtle animation&lt;/strong> you see when enabling/disabling this add-on
is pretty cool.&lt;/li>
&lt;li>Like the Firefox built-in &lt;em>Reader View&lt;/em>, &lt;em>Unclutter&lt;/em> also removes
distractions &amp;ndash; So &lt;strong>no ads or pop-ups&lt;/strong>!&lt;/li>
&lt;/ol>
&lt;p>It has other features like annotating pages with your notes privately
and displaying comments from &lt;a href="https://news.ycombinator.com/">Hacker News&lt;/a> and &lt;a href="https://web.hypothes.is/">Hypothes.is&lt;/a>, but I don&amp;rsquo;t
use those.&lt;/p>
&lt;h2 id="caveat">Caveat&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#caveat">#&lt;/a>&lt;/h2>
&lt;p>While viewing a page with &lt;em>Unclutter&lt;/em> enabled, there are no
issues. But I have noticed that if I toggle it off, the original CSS
gets broken. This issue is &lt;a href="https://github.com/lindylearn/unclutter/issues/25">tracked on its repo&lt;/a>. Until this gets fixed,
a quick workaround is to do &lt;code>Ctrl + F5&lt;/code> (force reload the page and
thus its CSS too) after disabling &lt;em>Unclutter&lt;/em>.&lt;/p>
&lt;h2 id="closing">Closing&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#closing">#&lt;/a>&lt;/h2>
&lt;p>But this minor issue doesn&amp;rsquo;t prevent it from making it my default
&amp;ldquo;reader view&amp;rdquo; app on Firefox.&lt;/p>
&lt;p>I&amp;rsquo;ll end this post with a before and after when visiting &lt;a href="https://www.masteringemacs.org/article/why-emacs-has-buffers">this
page&lt;/a>. Feel free to click those images to view them in higher
resolution.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="original.png">&lt;img src="original.png" alt="">&lt;/a>&lt;/td>
&lt;td>&lt;a href="uncluttered.png">&lt;img src="uncluttered.png" alt="">&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/web">web</category><category domain="https://scripter.co/tags/browser">browser</category><category domain="https://scripter.co/tags/reader-mode">reader-mode</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><category domain="https://scripter.co/tags/add-on">add-on</category><guid>https://scripter.co/unclutter-a-better-reader-view-for-browsers/</guid><pubDate>Sun, 12 Jun 2022 23:07:00 -0400</pubDate></item><item><title>Zero HTML Validation Errors!</title><link>https://scripter.co/zero-html-validation-errors/</link><description>&lt;blockquote>How I fixed my site content and went down from 46 HTML validations
errors down to 0!&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#avoid-duplicate-heading-id-attributes">&lt;span class="section-num">1&lt;/span> Avoid duplicate heading &lt;code>id&lt;/code> attributes&lt;/a>&lt;/li>
&lt;li>&lt;a href="#remove-inline-style-elements">&lt;span class="section-num">2&lt;/span> Remove inline &lt;code>&amp;lt;style&amp;gt;&lt;/code> elements&lt;/a>&lt;/li>
&lt;li>&lt;a href="#ensure-that-all-images-have-captions-or-alt-attributes">&lt;span class="section-num">3&lt;/span> Ensure that all images have captions or &lt;code>alt&lt;/code> attributes&lt;/a>&lt;/li>
&lt;li>&lt;a href="#do-not-have-hyperlinks-in-headings">&lt;span class="section-num">4&lt;/span> Do not have hyperlinks in headings&lt;/a>&lt;/li>
&lt;li>&lt;a href="#validation-ignores">Validation Ignores&lt;/a>
&lt;ul>
&lt;li>&lt;a href="#ignore-errors-due-to-hyperlinks-in-inline-svg">&lt;span class="section-num">5&lt;/span> Ignore errors due to hyperlinks in inline SVG&lt;/a>&lt;/li>
&lt;li>&lt;a href="#ignore-files-not-expected-to-serve-html-content">&lt;span class="section-num">6&lt;/span> Ignore files not expected to serve HTML content&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>In my &lt;a href="https://scripter.co/offline-html5-validator/">previous HTML5 Validator post&lt;/a>, I mentioned:&lt;/p>
&lt;blockquote>
&lt;p>I was a bit disappointed to see validation errors on my site, but then
it wasn&amp;rsquo;t too bad .. 46 errors.&lt;/p>
&lt;/blockquote>
&lt;p>But they truly say ..&lt;/p>
&lt;div class="org-center">
&lt;p>Ignorance is bliss.&lt;/p>
&lt;/div>
&lt;p>So .. once I realized that my site had 46 validation errors, I lost
that &lt;em>bliss&lt;/em> .. and I couldn&amp;rsquo;t rest easy &amp;mdash; I had to fix them all
😁.&lt;/p>
&lt;p>This post summarizes the categories of those errors and how I fixed
&lt;span class="sidenote-number">&lt;small class="sidenote">
The fixes mentioned in this post refer to changes in Org mode
content. But you should be able to derive equivalent fixes for
Markdown too.
&lt;/small>&lt;/span>
them all.&lt;/p>
&lt;h2 id="avoid-duplicate-heading-id-attributes">&lt;span class="section-num">1&lt;/span> Avoid duplicate heading &lt;code>id&lt;/code> attributes&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#avoid-duplicate-heading-id-attributes">#&lt;/a>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:636.34-636.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:300.34-300.52: error: Duplicate ID &amp;#34;older-issue&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:306.20-306.33: error: Duplicate ID &amp;#34;floats&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/page/6/index.html&amp;#34;:29.39-29.54: error: Duplicate ID &amp;#34;fnref:1&amp;#34;.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Errors with above kind of signatures were fixed by,&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Converting headings to description lists&lt;/p>
&lt;p>I had a bunch of generic headings like &amp;ldquo;Notes&amp;rdquo; and &amp;ldquo;Older Issue&amp;rdquo; in
some of my posts. After taking a second look at those, it made more
sense to convert those to description lists. So in Org mode, I
converted headings like &lt;code class="code-inline language-org">&lt;span class="gh">*&lt;/span>&lt;span class="gs"> Notes&lt;/span>&lt;/code> to
description lists &lt;code class="code-inline language-org">&lt;span class="k">- &lt;/span>Notes ::&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Setting &lt;code>CUSTOM_ID&lt;/code> heading property&lt;/p>
&lt;p>For the cases, where the headings needed to be left as so, their
IDs were uniquified by setting their &lt;code>CUSTOM_ID&lt;/code> property. For
example, below fixed the &lt;em>Duplicate ID &amp;ldquo;floats&amp;rdquo;&lt;/em> errors.
&lt;a id="code-snippet--using-custom-id-to-uniquify-heading-id">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl"> ** Precision
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *** Floats
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+:PROPERTIES:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+:CUSTOM_ID: precision-floats
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+:END:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">&lt;/span> ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ** Type (only for numbers)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> *** Floats
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+:PROPERTIES:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+:CUSTOM_ID: type-floats
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+:END:
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--using-custom-id-to-uniquify-heading-id">Code Snippet 1&lt;/a>:&lt;/span>
Using &lt;code>CUSTOM_ID&lt;/code> property to uniquify heading ID's
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Prevent footnote links in post summaries&lt;/p>
&lt;p>This issue was due to me not being conscious about how the footnote
references work in a post &lt;em>versus&lt;/em> on a page outside that post&amp;rsquo;s
context. The issue was caused by footnote references getting into
the post summaries parsed by Hugo, which will then show up on the
list pages.&lt;/p>
&lt;p>The fix was simple &amp;mdash; Edit the post summaries so that they don&amp;rsquo;t
contain any footnote references.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="remove-inline-style-elements">&lt;span class="section-num">2&lt;/span> Remove inline &lt;code>&amp;lt;style&amp;gt;&lt;/code> elements&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#remove-inline-style-elements">#&lt;/a>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​grep-po/index.html&amp;#34;:51.139-51.145: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​how-do-i-write-org-mode/index.html&amp;#34;:23.194-23.200: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Errors with above kind of signatures were fixed by,&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Avoiding export of raw &lt;code>&amp;lt;style&amp;gt;&lt;/code> elements in the Markdown content&lt;/p>
&lt;p>I figured out which functions were responsible for injecting
&lt;code>&amp;lt;style&amp;gt;&lt;/code> elements in Markdown content and then advised them to
stop that. After applying these advises, I lost the in-content
rules for CSS classes &lt;code>.org-center&lt;/code> and &lt;code>.csl-entry&lt;/code>. So I put
those rules directly in this website&amp;rsquo;s CSS.
&lt;a id="code-snippet--advices-to-prevent-style-elem-in-exports">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-emacs-lisp" data-lang="emacs-lisp">&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nb">defun&lt;/span> &lt;span class="nv">modi/org-blackfriday-center-block&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">_center-block&lt;/span> &lt;span class="nv">contents&lt;/span> &lt;span class="nv">info&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nb">let*&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nv">class&lt;/span> &lt;span class="s">&amp;#34;org-center&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nf">format&lt;/span> &lt;span class="s">&amp;#34;&amp;lt;div class=\&amp;#34;%s\&amp;#34;&amp;gt;%s\n\n%s\n&amp;lt;/div&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">class&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">org-blackfriday--extra-div-hack&lt;/span> &lt;span class="nv">info&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nv">contents&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nv">advice-add&lt;/span> &lt;span class="ss">&amp;#39;org-blackfriday-center-block&lt;/span> &lt;span class="nb">:override&lt;/span> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">modi/org-blackfriday-center-block&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nb">defun&lt;/span> &lt;span class="nv">modi/org-cite-csl-render-bibliography&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">bib-str&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nv">replace-regexp-in-string&lt;/span> &lt;span class="s">&amp;#34;&amp;lt;style&amp;gt;\\.csl-entry[^&amp;lt;]+&amp;lt;/style&amp;gt;&amp;#34;&lt;/span> &lt;span class="s">&amp;#34;&amp;#34;&lt;/span> &lt;span class="nv">bib-str&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="nv">advice-add&lt;/span> &lt;span class="ss">&amp;#39;org-cite-csl-render-bibliography&lt;/span> &lt;span class="nb">:filter-return&lt;/span> &lt;span class="nf">#&amp;#39;&lt;/span>&lt;span class="nv">modi/org-cite-csl-render-bibliography&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--advices-to-prevent-style-elem-in-exports">Code Snippet 2&lt;/a>:&lt;/span>
Emacs-Lisp advices to prevent &lt;code>&amp;lt;style&amp;gt;&lt;/code> elements in exports
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Removing unnecessary micro-styling&lt;/p>
&lt;p>I found a single case, where an inline CSS rule was defined in
content for CSS class &lt;code>.repr-type&lt;/code> for a table. I just removed that
without affecting the looks of that rendered table too much.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="ensure-that-all-images-have-captions-or-alt-attributes">&lt;span class="section-num">3&lt;/span> Ensure that all images have captions or &lt;code>alt&lt;/code> attributes&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#ensure-that-all-images-have-captions-or-alt-attributes">#&lt;/a>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​hugo-use-goat-code-blocks-for-ascii-diagrams/index.html&amp;#34;:24.130-24.255: error: An &amp;#34;img&amp;#34; element must have an &amp;#34;alt&amp;#34; attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Errors with above kind of signatures were easily fixed by ensuring
that all images had captions
&lt;span class="sidenote-number">&lt;small class="sidenote">
Thankfully, there were only two images that were missing captions.
&lt;/small>&lt;/span>
. The Hugo &lt;code>figure&lt;/code> shortcode adds the caption to the &lt;code>alt&lt;/code> attribute if
the &lt;code>alt&lt;/code> is not specified separately.&lt;/p>
&lt;p>As an example, here&amp;rsquo;s how I fixed the above error:&lt;/p>
&lt;p>&lt;a id="code-snippet--adding-a-caption-to-an-image">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ #+name: fig__disproportionate_box_drawing
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">+ #+caption: Disproportionate box drawing characters
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">&lt;/span>[[file:images/​hugo-use-goat-code-blocks-for-ascii-diagrams/ascii-diagram-rendered-in-plain-text-code-block.png]]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--adding-a-caption-to-an-image">Code Snippet 3&lt;/a>:&lt;/span>
A &lt;code>git diff&lt;/code> showing addition of caption to an image
&lt;/div>
&lt;h2 id="do-not-have-hyperlinks-in-headings">&lt;span class="section-num">4&lt;/span> Do not have hyperlinks in headings&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#do-not-have-hyperlinks-in-headings">#&lt;/a>&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​using-emacs-advice-to-silence-messages-from-functions/index.html&amp;#34;:151.687-151.732: error: Start tag &amp;#34;a&amp;#34; seen but an element of the same type was already open.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​using-emacs-advice-to-silence-messages-from-functions/index.html&amp;#34;:151.748-151.751: error: Stray end tag &amp;#34;a&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.446-114.475: error: Start tag &amp;#34;a&amp;#34; seen but an element of the same type was already open.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.446-114.475: error: End tag &amp;#34;a&amp;#34; violates nesting rules.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.526-114.529: error: Stray end tag &amp;#34;a&amp;#34;.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>While the hyperlinks in headings work well, they created invalid HTML
in the Hugo-generated TOC. So while these errors were created
technically because of a bug in Hugo
&lt;span class="sidenote-number">&lt;small class="sidenote">
It&amp;rsquo;s really cool when you end up finding a bug in an upstream project
while trying to fix the errors in your own thing 😎.
&lt;/small>&lt;/span>
, I wanted to fix these errors on my end as soon as I can.&lt;/p>
&lt;p>I reviewed the errors, and this is all it took to get rid of them all:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Remove manually inserting hyperlinks in headings
&lt;a id="code-snippet--removing-hyperlink-from-a-heading">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl"> show two methods of finding sources of any printed messages.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">-***** Using plain-old /grep/ or [[https://github.com/BurntSushi/ripgrep][/rg/]]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">&lt;/span>&lt;span class="gi">+***** Using plain-old /grep/ or /ripgrep (rg)/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">&lt;/span> This method is pretty easy (but not robust) to use if the search
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Org source directory and search for the /&amp;#34;org-babel-exp process ..&amp;#34;/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">-string ..
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">&lt;/span>&lt;span class="gi">+string using [[https://github.com/BurntSushi/ripgrep][~rg~]] ..
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--removing-hyperlink-from-a-heading">Code Snippet 4&lt;/a>:&lt;/span>
Removing hyperlink from a heading
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Remove Org Radio links that created links in headings&lt;/p>
&lt;p>Here, a Org heading happened to contain the string &amp;ldquo;Day count&amp;rdquo;,
which was also an &lt;a href="https://orgmode.org/manual/Radio-Targets.html" title="Emacs Lisp: (info &amp;quot;(org) Radio Targets&amp;quot;)">an Org Radio link&lt;/a> in that post. While ideally
that shouldn&amp;rsquo;t have mattered, I removed that radio link to get
around this Hugo bug.
&lt;a id="code-snippet--removing-an-org-radio-link">&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-diff" data-lang="diff">&lt;span class="line">&lt;span class="cl"> .. /Just may be/. But regardless, I am already enjoying writing once
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">-again, and it&amp;#39;s great to see the &amp;lt;&amp;lt;&amp;lt;Day count&amp;gt;&amp;gt;&amp;gt; (counting up to 100)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gd">&lt;/span>&lt;span class="gi">+again, and it&amp;#39;s great to see the Day count (counting up to 100)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gi">&lt;/span> increase with each new post!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--removing-an-org-radio-link">Code Snippet 5&lt;/a>:&lt;/span>
Removing an Org Radio link
&lt;/div>
&lt;/li>
&lt;/ol>
&lt;h2 id="validation-ignores">Validation Ignores&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#validation-ignores">#&lt;/a>&lt;/h2>
&lt;p>Above fixes fixed 43 out of 46 errors, but the remaining 3 were unfixable.&lt;/p>
&lt;h3 id="ignore-errors-due-to-hyperlinks-in-inline-svg">&lt;span class="section-num">5&lt;/span> Ignore errors due to hyperlinks in inline SVG&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#ignore-errors-due-to-hyperlinks-in-inline-svg">#&lt;/a>&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​plantuml/index.html&amp;#34;:114.474-114.678: error: Attribute &amp;#34;title&amp;#34; not allowed on element &amp;#34;a&amp;#34; at this point.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This error was caused by hyperlinks in inline SVG elements. These SVG
elements are created by &lt;a href="https://plantuml.com/">PlantUML&lt;/a>. The &lt;em>hyperlinks in SVG&lt;/em> feature
works great, and as these are generated by PlantUML, I chose to just
ignore these errors.&lt;/p>
&lt;p>I ignored this error by adding the &lt;code>--ignore-re 'notes/plantuml.*Attribute.*title.*not allowed'&lt;/code> switch to the
&lt;code>html5validator&lt;/code> command.&lt;/p>
&lt;h3 id="ignore-files-not-expected-to-serve-html-content">&lt;span class="section-num">6&lt;/span> Ignore files not expected to serve HTML content&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#ignore-files-not-expected-to-serve-html-content">#&lt;/a>&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/googleFOO.html&amp;#34;:1.1-1.52: error: Non-space characters found without seeing a doctype first. Expected &amp;#34;&amp;lt;!DOCTYPE html&amp;gt;&amp;#34;.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>googleFOO.html&lt;/code> file here is not a valid HTML file. It&amp;rsquo;s a just a
&lt;em>cookie&lt;/em> file that was used by Google to verify that I own this
domain.&lt;/p>
&lt;p>This error was masked by adding the &lt;code>--ignore 'googleFOO'&lt;/code> switch to
the &lt;code>html5validator&lt;/code> command.&lt;/p>
&lt;h2 id="summary">Summary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#summary">#&lt;/a>&lt;/h2>
&lt;p>Once I fixed the 43 errors by tweaking the Org mode content, and added
those two ignores, I had &lt;strong>zero validation errors&lt;/strong>! 🎉&lt;/p>
&lt;p>If you are interested in the fix details, &lt;a href="https://gitlab.com/kaushalmodi/kaushalmodi.gitlab.io/-/compare/a7cac5dd1293442a51fd5020a3bcef8da7f75fdc...42d6c72d533c337b5c67fa09207f68e45efe6e6c">here are the commits&lt;/a>.&lt;/p></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/emacs">emacs</category><category domain="https://scripter.co/categories/web">web</category><category domain="https://scripter.co/categories/org">org</category><category domain="https://scripter.co/series/html5-validator">HTML5 Validator</category><category domain="https://scripter.co/tags/html">html</category><category domain="https://scripter.co/tags/validator">validator</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/zero-html-validation-errors/</guid><pubDate>Sun, 05 Jun 2022 17:58:00 -0400</pubDate></item><item><title>Offline HTML5 Validator</title><link>https://scripter.co/offline-html5-validator/</link><description>&lt;blockquote>Validate your website offline &amp;mdash; It&amp;rsquo;s just one &lt;code>curl&lt;/code> command away.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#using-the-dot-jar">Using the &lt;code>.jar&lt;/code>&lt;/a>
&lt;ul>
&lt;li>&lt;a href="#using-pip-install">Using &lt;code>pip install&lt;/code>&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="#using-pre-compiled-binary">Using pre-compiled binary&lt;/a>&lt;/li>
&lt;li>&lt;a href="#results">Results&lt;/a>&lt;/li>
&lt;li>&lt;a href="#conclusion">Conclusion&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>I have been using the online HTML5 Validator
&lt;a href="https://html5.validator.nu/">https://html5.validator.nu/&lt;/a> for few years now. I have a link at the
bottom of each post to validate that page&amp;rsquo;s HTML. For an example, see
the &lt;em>html5 validator&lt;/em> link at the &lt;a href="#bottom">bottom&lt;/a> of this
post.&lt;/p>
&lt;p>But it didn&amp;rsquo;t occur to me until now to look for a way to do the same
validation offline! Offline validation would be useful so that I can
look at any HTML generation problem before I deploy the website. So I
looked for a solution online, and of course it&amp;rsquo;s &lt;a href="https://stackoverflow.com/a/26505206/1219634">answered on
StackOverflow&lt;/a> 😄.&lt;/p>
&lt;p>It turns out that same &lt;a href="https://github.com/validator/validator">Nu HTML5 Validator&lt;/a> project that provides the
online validation service, also provides a Java application as well as
pre-compiled binaries for Linux, Windows and MacOS for offline use!&lt;/p>
&lt;p>To use the Java &lt;em>.jar&lt;/em> file, you need to have at least Java 8
installed on your system. But you don&amp;rsquo;t need to have any version of
Java installed if you use the pre-compiled binary instead. See &lt;a href="https://validator.github.io/validator/">its
documentation&lt;/a> for more details.&lt;/p>
&lt;h2 id="using-the-dot-jar">Using the &lt;code>.jar&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#using-the-dot-jar">#&lt;/a>&lt;/h2>
&lt;div class="note">
&lt;p>Requires at least Java 8&lt;/p>
&lt;/div>
&lt;dl>
&lt;dt>Download&lt;/dt>
&lt;dd>Download the latest &lt;code>vnu.jar&lt;/code> from the project&amp;rsquo;s &lt;a href="https://github.com/validator/validator/releases">GitHub
Releases section&lt;/a>.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl -ROLs https://github.com/validator/validator/releases/download/latest/vnu.jar
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/dd>
&lt;dt>Run&lt;/dt>
&lt;dd>Below command runs the validator only on the HTML files in
the &lt;code>public/&lt;/code>
&lt;span class="sidenote-number">&lt;small class="sidenote">
If you are using &lt;a href="https://gohugo.io">Hugo&lt;/a>, the &lt;code>hugo&lt;/code> command will publish the HTML
files in the &lt;code>public/&lt;/code> directory by default.
&lt;/small>&lt;/span>
directory. See its &lt;a href="https://validator.github.io/validator/#usage">Usage documentation&lt;/a> for more details.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">java -jar vnu.jar --skip-non-html --errors-only public/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For my usecase, if I don&amp;rsquo;t provide the &lt;code>--skip-non-html --errors-only&lt;/code> switches, the output is too noisy.&lt;/p>
&lt;/dd>
&lt;/dl>
&lt;h3 id="using-pip-install">Using &lt;code>pip install&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#using-pip-install">#&lt;/a>&lt;/h3>
&lt;p>If you do not want to manually download the &lt;code>.jar&lt;/code>, there&amp;rsquo;s a Python
wrapper available to do the same for you: &lt;a href="https://github.com/svenkreiss/html5validator">html5validator&lt;/a>.&lt;/p>
&lt;dl>
&lt;dt>Install&lt;/dt>
&lt;dd>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> pip install --user html5validator
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/dd>
&lt;dt>Run&lt;/dt>
&lt;dd>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> html5validator --root public/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>It seems like this Python wrapper implicitly passes &lt;code>--skip-non-html --errors-only&lt;/code> to the Java app. So those are not needed when running
&lt;code>html5validator&lt;/code>. But on the flip side, it needs the &lt;code>--root&lt;/code> switch
when specifying the directory to run the script on.&lt;/dd>
&lt;/dl>
&lt;p>Note that you still need to have at least Java 8 installed when
running this Python app too, because it downloads and run the same
&lt;code>.jar&lt;/code> behind the scenes.&lt;/p>
&lt;h2 id="using-pre-compiled-binary">Using pre-compiled binary&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#using-pre-compiled-binary">#&lt;/a>&lt;/h2>
&lt;p>If your system doesn&amp;rsquo;t have the required Java version, you can use the
pre-compiled binary instead.&lt;/p>
&lt;dl>
&lt;dt>Download &amp;amp; Extract&lt;/dt>
&lt;dd>Download and extract the &lt;code>vnu.&amp;lt;OS&amp;gt;.zip&lt;/code> for
your &lt;strong>OS&lt;/strong> from the same &lt;em>Releases&lt;/em> section. Here, I am showing how
to do that on Linux:
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">curl -ROLs https://github.com/validator/validator/releases/download/latest/vnu.linux.zip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">unzip vnu.linux.zip
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The extracted binary path will be &lt;code>vnu-runtime-image/bin/vnu&lt;/code>.&lt;/p>
&lt;/dd>
&lt;dt>Run&lt;/dt>
&lt;dd>The run options will be the exact same; just that you will be
running the binary directly instead of running through &lt;code>java&lt;/code>.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">vnu-runtime-image/bin/vnu --skip-non-html --errors-only public/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/dd>
&lt;/dl>
&lt;h2 id="results">Results&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#results">#&lt;/a>&lt;/h2>
&lt;div class="logbook-notes">
&lt;dl>
&lt;dt>
&lt;span class="timestamp-wrapper">
&lt;span class="timestamp">
&amp;lt;2022-06-05&amp;gt;
&lt;/span>
&lt;/span>
&lt;/dt>
&lt;dd>
This website now has zero validation errors! 🎉 All the errors
listed in the &lt;a href="#org-target--validation-log">collapsed log below&lt;/a> are now resolved. See my &lt;a href="https://scripter.co/zero-html-validation-errors/">Zero HTML Validation Errors!&lt;/a> post on how I did that.
&lt;/dd>
&lt;/dl>
&lt;/div>
&lt;p>I was a bit disappointed to see validation errors on my site, but then
it wasn&amp;rsquo;t too bad .. &lt;del>52&lt;/del> 46 errors:&lt;/p>
&lt;dl>
&lt;dt>Some I already fixed&lt;/dt>
&lt;dd>These 6 errors were fixed in &lt;a href="https://gitlab.com/kaushalmodi/kaushalmodi.gitlab.io/-/commit/4288a70fa473800b597d46c77bf4021d7c0cd060">this commit&lt;/a>.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​getting-started-with-texlive/index.html&amp;#34;:6.2198-6.2206: error: Element &amp;#34;package&amp;#34; not allowed as child of element &amp;#34;li&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​getting-started-with-texlive/index.html&amp;#34;:6.2256-6.2259: error: End tag &amp;#34;li&amp;#34; implied, but there were open elements.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​getting-started-with-texlive/index.html&amp;#34;:6.2198-6.2206: error: Unclosed element &amp;#34;package&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​getting-started-with-texlive/index.html&amp;#34;:6.2307-6.2315: error: Element &amp;#34;package&amp;#34; not allowed as child of element &amp;#34;li&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​getting-started-with-texlive/index.html&amp;#34;:6.2316-6.2319: error: End tag &amp;#34;li&amp;#34; implied, but there were open elements.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​getting-started-with-texlive/index.html&amp;#34;:6.2307-6.2315: error: Unclosed element &amp;#34;package&amp;#34;.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/dd>
&lt;dt>Some I can probably fix&lt;/dt>
&lt;dd>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"> &amp;#34;file:/public/notes/​nim/index.html&amp;#34;:472.480-472.486: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;file:/public/notes/​nim/index.html&amp;#34;:1359.221-1359.231: error: Duplicate ID &amp;#34;log&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ..
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/dd>
&lt;dt>Some need to be ignored&lt;/dt>
&lt;dd>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"> &amp;#34;file:/public/​google4a938eaf9bbacbcd.html&amp;#34;:1.1-1.52: error: Non-space characters found without seeing a doctype first. Expected &amp;#34;&amp;lt;!DOCTYPE html&amp;gt;&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;file:/public/​google4a938eaf9bbacbcd.html&amp;#34;:1.1-1.52: error: Element &amp;#34;head&amp;#34; is missing a required instance of child element &amp;#34;title&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ..
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/dd>
&lt;dt>And the rest would be out of my scope to fix&lt;/dt>
&lt;dd>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"> &amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.446-114.475: error: Start tag &amp;#34;a&amp;#34; seen but an element of the same type was already open.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.446-114.475: error: End tag &amp;#34;a&amp;#34; violates nesting rules.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.526-114.529: error: Stray end tag &amp;#34;a&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ..
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/dd>
&lt;/dl>
&lt;p>Expand the below drawer if you&amp;rsquo;d like to see the full log with 46
errors: &lt;span class="org-target" id="org-target--validation-log">&lt;/span>&lt;/p>
&lt;details>
&lt;summary>Output of running &lt;code>html5validator --root public/&lt;/code>&lt;/summary>
&lt;div class="details">
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​google4a938eaf9bbacbcd.html&amp;#34;:1.1-1.52: error: Non-space characters found without seeing a doctype first. Expected &amp;#34;&amp;lt;!DOCTYPE html&amp;gt;&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​google4a938eaf9bbacbcd.html&amp;#34;:1.1-1.52: error: Element &amp;#34;head&amp;#34; is missing a required instance of child element &amp;#34;title&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​plantuml/index.html&amp;#34;:114.474-114.678: error: Attribute &amp;#34;title&amp;#34; not allowed on element &amp;#34;a&amp;#34; at this point.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:300.34-300.52: error: Duplicate ID &amp;#34;older-issue&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:306.20-306.33: error: Duplicate ID &amp;#34;floats&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:339.34-339.52: error: Duplicate ID &amp;#34;older-issue&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:341.320-341.335: error: Duplicate ID &amp;#34;integers&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:341.494-341.507: error: Duplicate ID &amp;#34;floats&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim-fmt/index.html&amp;#34;:385.34-385.48: error: Duplicate ID &amp;#34;strings&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:134.34-134.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:239.34-239.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:269.34-269.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:336.34-336.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:513.106-513.118: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:588.106-588.118: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:636.34-636.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:731.34-731.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:797.34-797.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:846.34-846.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:894.34-894.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:920.34-920.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:942.34-942.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:1012.34-1012.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​string-fns-nim-vs-python/index.html&amp;#34;:1074.34-1074.46: error: Duplicate ID &amp;#34;notes&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:472.480-472.486: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:1359.221-1359.231: error: Duplicate ID &amp;#34;log&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:2364.130-2364.149: error: Duplicate ID &amp;#34;named-tuples&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:2403.149-2403.172: error: Duplicate ID &amp;#34;anonymous-tuples&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:3370.284-3370.303: error: Duplicate ID &amp;#34;installation&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:3410.354-3410.373: error: Duplicate ID &amp;#34;installation&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:5033.34-5033.52: error: Duplicate ID &amp;#34;older-issue&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:5376.34-5376.45: error: Duplicate ID &amp;#34;json&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/notes/​nim/index.html&amp;#34;:6066.64-6066.81: error: Duplicate ID &amp;#34;references&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/bits/​plantuml-version/index.html&amp;#34;:7.37-7.94: error: An &amp;#34;img&amp;#34; element must have an &amp;#34;alt&amp;#34; attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.446-114.475: error: Start tag &amp;#34;a&amp;#34; seen but an element of the same type was already open.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.446-114.475: error: End tag &amp;#34;a&amp;#34; violates nesting rules.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​auto-count-100daystooffload-posts/index.html&amp;#34;:114.526-114.529: error: Stray end tag &amp;#34;a&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​generics-not-exactly-in-systemverilog/index.html&amp;#34;:118.232-118.238: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​grep-po/index.html&amp;#34;:51.139-51.145: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​how-do-i-write-org-mode/index.html&amp;#34;:23.194-23.200: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​hugo-use-goat-code-blocks-for-ascii-diagrams/index.html&amp;#34;:24.130-24.255: error: An &amp;#34;img&amp;#34; element must have an &amp;#34;alt&amp;#34; attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​hugo-modules-getting-started/index.html&amp;#34;:6.865-6.871: error: Element &amp;#34;style&amp;#34; not allowed as child of element &amp;#34;div&amp;#34; in this context. (Suppressing further errors from this subtree.)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​using-emacs-advice-to-silence-messages-from-functions/index.html&amp;#34;:151.687-151.732: error: Start tag &amp;#34;a&amp;#34; seen but an element of the same type was already open.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/​using-emacs-advice-to-silence-messages-from-functions/index.html&amp;#34;:151.748-151.751: error: Stray end tag &amp;#34;a&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/page/6/index.html&amp;#34;:29.39-29.54: error: Duplicate ID &amp;#34;fnref:1&amp;#34;.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;file:/public/page/6/index.html&amp;#34;:49.169-49.184: error: Duplicate ID &amp;#34;fnref:1&amp;#34;.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>
&lt;/details>
&lt;h2 id="conclusion">Conclusion&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#conclusion">#&lt;/a>&lt;/h2>
&lt;p>It was really easy to download the run the &lt;code>vnu&lt;/code> application using
Java, the standalone Linux binary and also through the
&lt;code>html5validator&lt;/code> Python wrapper.&lt;/p>
&lt;p>After my quick trials, I think I will use the &lt;code>html5validator&lt;/code>
approach more because,&lt;/p>
&lt;ol>
&lt;li>It works as I expect will the least number of switches.&lt;/li>
&lt;li>I am able to redirect the output using &lt;code>html5validator --root public/ &amp;gt; validate.log&lt;/code>. &lt;em>I tried the same using the &lt;code>vnu.jar&lt;/code> and
Linux compiled &lt;code>vnu&lt;/code> binary, but the error log redirection didn&amp;rsquo;t
work with those.&lt;/em>&lt;/li>
&lt;/ol></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/web">web</category><category domain="https://scripter.co/series/html5-validator">HTML5 Validator</category><category domain="https://scripter.co/tags/html">html</category><category domain="https://scripter.co/tags/validator">validator</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><guid>https://scripter.co/offline-html5-validator/</guid><pubDate>Wed, 01 Jun 2022 00:11:00 -0400</pubDate></item><item><title>Hugo Modules: Importing a Theme</title><link>https://scripter.co/hugo-modules-importing-a-theme/</link><description>&lt;blockquote>A brief guide on how to install Hugo themes using Hugo Modules.&lt;/blockquote>&lt;div class="ox-hugo-toc toc">
&lt;div class="heading">Table of Contents&lt;/div>
&lt;ul>
&lt;li>&lt;a href="#clean-up-the-old-way-of-setting-a-theme">&lt;span class="section-num">1&lt;/span> Clean up the old way of setting a theme&lt;/a>&lt;/li>
&lt;li>&lt;a href="#import-the-theme-module">&lt;span class="section-num">2&lt;/span> Import the &amp;ldquo;theme&amp;rdquo; module&lt;/a>
&lt;ul>
&lt;li>&lt;a href="#module-imports-example">Quick Example&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="#hugo-mod-tidy">&lt;span class="section-num">3&lt;/span> &lt;code>hugo mod tidy&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="#updating-the-theme">&lt;span class="section-num">4&lt;/span> Updating the theme&lt;/a>&lt;/li>
&lt;li>&lt;a href="#dependency-graph">Dependency Graph&lt;/a>&lt;/li>
&lt;li>&lt;a href="#building-your-hugo-site-on-a-server">Building your Hugo site on a server&lt;/a>&lt;/li>
&lt;li>&lt;a href="#in-a-nutshell">In a nutshell&lt;/a>&lt;/li>
&lt;li>&lt;a href="#references">References&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;!--endtoc-->
&lt;p>Hello! You are reading this post because you are probably interested
in the Hugo Modules feature and are considering to import a Hugo
Module as a theme.&lt;/p>
&lt;p>&lt;strong>Step 0&lt;/strong> for that approach is to make your site repo a Hugo Module. If
your site already is, then it would have a &lt;code>go.mod&lt;/code> file in the repo
root. If you don&amp;rsquo;t have the &lt;code>go.mod&lt;/code> file, check out the previous post
&lt;a href="https://scripter.co/hugo-modules-getting-started/">Hugo Modules: Getting Started&lt;/a> first ‼️&lt;/p>
&lt;div class="note">
&lt;p>If you don&amp;rsquo;t have a &lt;code>go.mod&lt;/code> file for your site repo, and you still
decide to continue with the next steps, don&amp;rsquo;t complain if you see
errors like &lt;em>module &amp;ldquo;foo&amp;rdquo; not found; either add it as a Hugo Module or
store it in &amp;ldquo;&amp;lt;your site repo&amp;gt;/themes&amp;rdquo;.: module does not exist&lt;/em>. &amp;mdash;
&lt;a href="https://discourse.gohugo.io/t/hugo-mod-init-fails-to-create-go-mod-if-hugo-detects-an-error-prematurely-in-site-config-toml/36687">speaking from experience&lt;/a> 😉.&lt;/p>
&lt;/div>
&lt;p>With that out of the way, here are the next steps ..&lt;/p>
&lt;h2 id="clean-up-the-old-way-of-setting-a-theme">&lt;span class="section-num">1&lt;/span> Clean up the old way of setting a theme&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#clean-up-the-old-way-of-setting-a-theme">#&lt;/a>&lt;/h2>
&lt;p>If you are upgrading your Hugo site to switch from the legacy method
of using themes (i.e using the &lt;code>theme&lt;/code> variable in the site config
&lt;span class="sidenote-number">&lt;small class="sidenote">
In my posts, you may have seen me use the &lt;em>Site Config&lt;/em> term or
&lt;code>config.toml&lt;/code> &amp;ndash; They mean the same thing.
&lt;/small>&lt;/span>
), you need to clean that up.&lt;/p>
&lt;ol>
&lt;li>Remove the &lt;code>theme&lt;/code> variable from your site config.&lt;/li>
&lt;li>Remove the &lt;code>themes&lt;/code> directory, or move it out of your Hugo site
repo.
&lt;ul>
&lt;li>If you were cloning a theme developed by someone else in there,
you can just remove this directory.&lt;/li>
&lt;li>If you are maintaining your own theme in that directory, move it
out of your site repo and &lt;a href="https://scripter.co/hugo-modules-getting-started/#convert-to-hugo-module">convert it to a Hugo Module&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="import-the-theme-module">&lt;span class="section-num">2&lt;/span> Import the &amp;ldquo;theme&amp;rdquo; module&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#import-the-theme-module">#&lt;/a>&lt;/h2>
&lt;p>The &lt;em>theme&lt;/em> is quoted in this title, because the concept of a Hugo
&amp;ldquo;theme&amp;rdquo; is a bit old now (&lt;span class="timestamp-wrapper">&lt;span class="timestamp">&amp;lt;2022-05-26 Thu&amp;gt;&lt;/span>&lt;/span>) and that has been
superseded with the concept of &amp;ldquo;modules&amp;rdquo;.&lt;/p>
&lt;p>The main difference between a theme and a generic &lt;em>Hugo Module&lt;/em> is
that the former will allow you to build your site entirely, while the
latter might implement only some modular features like enabling the
ATOM feed, or adding a search to your website.&lt;/p>
&lt;div class="note">
&lt;p>I am mentioning this again for convenience, from &lt;a href="https://scripter.co/hugo-modules-getting-started/">the previous post in
this series&lt;/a>:&lt;/p>
&lt;p>&lt;em>A module can be your main project or a smaller module providing one
or more of the 7 component types defined in Hugo: &lt;strong>static&lt;/strong>, &lt;strong>content&lt;/strong>,
&lt;strong>layouts&lt;/strong>, &lt;strong>data&lt;/strong>, &lt;strong>assets&lt;/strong>, &lt;strong>i18n&lt;/strong>, and &lt;strong>archetypes&lt;/strong>. You can combine
modules in any combination you like, and even mount directories from
non-Hugo projects, forming a big, virtual union file system.&lt;/em>&lt;/p>
&lt;/div>
&lt;p>A theme will need to have the &amp;ldquo;layout&amp;rdquo; component. Additionally, it
might have the &amp;ldquo;assets&amp;rdquo;, &amp;ldquo;static&amp;rdquo;, and other components too.&lt;/p>
&lt;p>Importing a module as a theme will typically look like this in your
site config:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[[&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">imports&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;URL of the theme&amp;#39;s git remote *without* the &amp;#39;https://&amp;#39; part&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;strong>path&lt;/strong> here would be something like
&lt;code>github.com/USER/THEME-REPO-NAME&lt;/code> or
&lt;code>gitlab.com/USER/THEME-REPO-NAME&lt;/code>.&lt;/p>
&lt;dl>
&lt;dt>Note&lt;/dt>
&lt;dd>It&amp;rsquo;s possible to take &lt;span class="underline">any&lt;/span> Hugo theme git repo and import
that as a Hugo Module even if that repo isn&amp;rsquo;t actually one
i.e. doesn&amp;rsquo;t have a &lt;code>go.mod&lt;/code>. But it&amp;rsquo;s recommended that the theme be
a proper Hugo Module so that you have better dependency tracking
between your site and the theme.&lt;/dd>
&lt;/dl>
&lt;h3 id="module-imports-example">Quick Example&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#module-imports-example">#&lt;/a>&lt;/h3>
&lt;p>Follow these steps if you want to try out how this Hugo Module based
theme importing&lt;/p>
&lt;div class="note">
&lt;p>As a reminder, you &lt;a href="https://scripter.co/hugo-modules-getting-started/#install-a-recent-version-of-go">need to have Go installed&lt;/a>
on your system.&lt;/p>
&lt;/div>
&lt;ol>
&lt;li>Create a temporary directory somewhere and &lt;code>cd&lt;/code> to it.&lt;/li>
&lt;li>Initialize your site as a Hugo Module: &lt;code>hugo mod init foo&lt;/code> (yeah,
type that out literally &amp;mdash; it will work)&lt;/li>
&lt;li>Create a &lt;code>config.toml&lt;/code> file with the below content. It imports the
&lt;a href="https://gitlab.com/kaushalmodi/hugo-mwe-theme">&lt;code>hugo-mwe-theme&lt;/code>&lt;/a>
&lt;span class="sidenote-number">&lt;small class="sidenote">
&lt;code>hugo-mwe-theme&lt;/code> is a minimal Hugo theme that I use to quickly try
out some new feature in Hugo or to create a &lt;em>minimal working
example&lt;/em> to reproduce a bug.
&lt;/small>&lt;/span>
theme.
&lt;a id="code-snippet--theme-module-import-example">&lt;/a>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[[&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">imports&lt;/span>&lt;span class="p">]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;gitlab.com/kaushalmodi/hugo-mwe-theme&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="src-block-caption">
&lt;span class="src-block-number">&lt;a href="#code-snippet--theme-module-import-example">Code Snippet 1&lt;/a>:&lt;/span>
Example of importing a Hugo module as a theme in &lt;code>config.toml&lt;/code>
&lt;/div>
&lt;/li>
&lt;li>Create &lt;code>content/hello.md&lt;/code>. This step is optional and is only so
that your test site as some content.
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-md" data-lang="md">&lt;span class="line">&lt;span class="cl">+++
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">title = &amp;#34;Hello&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+++
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hey!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>That&amp;rsquo;s it! Now run the Hugo server (&lt;code>hugo server&lt;/code>) and look at your
site running on localhost .. while thinking in disbelief.. &lt;em>just how
easy all of this was!&lt;/em> 😃.&lt;/p>
&lt;ul>
&lt;li>Did you need to manually clone any theme? &lt;strong>No&lt;/strong>&lt;/li>
&lt;li>Would you need to deal with the &lt;code>.gitmodules&lt;/code> file? &lt;strong>No&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h2 id="hugo-mod-tidy">&lt;span class="section-num">3&lt;/span> &lt;code>hugo mod tidy&lt;/code>&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#hugo-mod-tidy">#&lt;/a>&lt;/h2>
&lt;p>Finally, run &lt;a href="https://gohugo.io/commands/hugo_mod_tidy/">&lt;code>hugo mod tidy&lt;/code>&lt;/a> to clean up the &lt;code>go.mod&lt;/code> and
update/generate the &lt;code>go.sum&lt;/code> file. These files will track the module
dependencies for your site.&lt;/p>
&lt;ul>
&lt;li>The &lt;code>go.mod&lt;/code> contains the direct module dependencies for your site.&lt;/li>
&lt;li>The &lt;code>go.sum&lt;/code> contains the versions and hashes of all the direct &lt;strong>and
indirect&lt;/strong> dependencies
&lt;span class="sidenote-number">&lt;small class="sidenote">
Just as you added a theme as a Hugo Module to your site, it&amp;rsquo;s
possible that that theme is depending on other Hugo Modules (like
the ones I mentioned earlier: ATOM feeds, search, etc.).
&lt;/small>&lt;/span>
for your site.&lt;/li>
&lt;/ul>
&lt;div class="note">
&lt;p>You would need to commit the &lt;code>go.mod&lt;/code> and &lt;code>go.sum&lt;/code>&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> files if you
build and deploy your website on a remote server or a &lt;a href="https://en.wikipedia.org/wiki/CI/CD">CI/CD&lt;/a> system.&lt;/p>
&lt;/div>
&lt;p>If you ran the &lt;a href="#module-imports-example">Quick Example&lt;/a>, you will see this (as of
&lt;span class="timestamp-wrapper">&lt;span class="timestamp">&amp;lt;2022-05-26 Thu&amp;gt;&lt;/span>&lt;/span>) in your &lt;code>go.mod&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">module foo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">go 1.18
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">require gitlab.com/kaushalmodi/hugo-mwe-theme v0.1.1 // indirect
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>.. and this in your &lt;code>go.sum&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">gitlab.com/kaushalmodi/hugo-mwe-theme v0.1.1 h1:FyTp43CJRpBfoHyWnwQFx//cipgP6xQ9/uucj+qjj1U=
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gitlab.com/kaushalmodi/hugo-mwe-theme v0.1.1/go.mod h1:vvq0r/SfKMbiPbyqL4YottSOkpCkBSosqGRm82aDNrU=
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="updating-the-theme">&lt;span class="section-num">4&lt;/span> Updating the theme&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#updating-the-theme">#&lt;/a>&lt;/h2>
&lt;p>Here are some common ways to update the theme module going forward:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Command&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>hugo mod get -u&lt;/code>&lt;/td>
&lt;td>Update only the modules that your site directly depends on.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hugo mod get -u ./...&lt;/code>&lt;/td>
&lt;td>Update the modules that your site depends on in a recursive fashion.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Additionally, you might or might not need these, but I am documenting
them here for completeness:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Command&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>hugo mod get -u &amp;lt;module path&amp;gt;&lt;/code>&lt;/td>
&lt;td>Update only the specified module&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup> to the latest version. Example: &lt;code>hugo mod get -u gitlab.com/kaushalmodi/hugo-mwe-theme&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hugo mod get &amp;lt;module path&amp;gt;@&amp;lt;git ref&amp;gt;&lt;/code>&lt;/td>
&lt;td>Update a module to the specified git tag or commit. Example: &lt;code>hugo mod get gitlab.com/kaushalmodi/hugo-mwe-theme@v0.1.1&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="dependency-graph">Dependency Graph&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#dependency-graph">#&lt;/a>&lt;/h2>
&lt;p>If you have a theme added as a Hugo Module, which depends on other
Hugo Modules, it&amp;rsquo;s often helpful to know the dependency graph. You can
do that by running:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">hugo mod graph
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For the above &lt;a href="#module-imports-example">Quick Example&lt;/a>, you will see just this one line because
that theme does not depend on other modules:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">foo gitlab.com/kaushalmodi/hugo-mwe-theme@v0.1.1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;h2 id="building-your-hugo-site-on-a-server">Building your Hugo site on a server&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#building-your-hugo-site-on-a-server">#&lt;/a>&lt;/h2>
&lt;p>Alright, so you are able to build your site locally after switching to
using themes as modules, great!&lt;/p>
&lt;p>Now, if you build and deploy your site on a remote server like Netlify
or Vercel, you need to ensure that you have a recent version of Go
installed in their environment too.&lt;/p>
&lt;p>I deploy this website using Netlify, and so I know how to do that
there &amp;mdash; Set the &lt;code>GO_VERSION&lt;/code> environment variable to a recent
version like &lt;strong>1.18&lt;/strong> in the &lt;a href="https://docs.netlify.com/configure-builds/environment-variables/">Environment variables&lt;/a> section in Netlify
&lt;em>Build &amp;amp; deploy&lt;/em> settings.&lt;/p>
&lt;h2 id="in-a-nutshell">In a nutshell&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#in-a-nutshell">#&lt;/a>&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>First&lt;/strong> convert your Hugo site to a Hugo module.&lt;/li>
&lt;li>Then replace the &lt;code>theme&lt;/code> in your site config with a module import.&lt;/li>
&lt;/ol>
&lt;p>Enjoy! 🍾&lt;/p>
&lt;h2 id="references">References&amp;nbsp;&lt;a class="headline-hash no-text-decoration" href="#references">#&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://gohugo.io/hugo-modules/use-modules/">Hugo Modules documentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.thenewdynamic.com/article/hugo-modules-everything-from-imports-to-create/">Hugo Modules: Everything you need to know!&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>It is recommended to &lt;em>git commit&lt;/em> the &lt;code>go.sum&lt;/code> along with your
site&amp;rsquo;s &lt;code>go.mod&lt;/code>. From the &lt;a href="https://github.com/golang/go/wiki/Modules#releasing-modules-all-versions">Go Modules documention&lt;/a>: &lt;em>Ensure your
&lt;code>go.sum&lt;/code> file is committed along with your &lt;code>go.mod&lt;/code> file. See &lt;a href="https://github.com/golang/go/wiki/Modules#should-i-commit-my-gosum-file-as-well-as-my-gomod-file">FAQ
below&lt;/a> for more details and rationale.&lt;/em>&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Trust me.. once you get a hang of the Hugo Module system, your
site will have more than one!&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description><author>Kaushal.Modi@fakeEmailToMakeValidatorHappy.com (Kaushal Modi)</author><category domain="https://scripter.co/categories/hugo">hugo</category><category domain="https://scripter.co/series/hugo-modules">Hugo Modules</category><category domain="https://scripter.co/tags/module">module</category><category domain="https://scripter.co/tags/100daystooffload">100DaysToOffload</category><category domain="https://scripter.co/tags/theme">theme</category><category domain="https://scripter.co/tags/component">component</category><guid>https://scripter.co/hugo-modules-importing-a-theme/</guid><pubDate>Thu, 26 May 2022 16:26:00 -0400</pubDate></item></channel></rss>