aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/art/index.html37
-rw-r--r--docs/art/lapse.pngbin0 -> 3756 bytes
-rw-r--r--docs/index.html12
-rw-r--r--docs/index.xml2384
-rw-r--r--docs/posts/SDL2_devlog/index.html240
-rw-r--r--docs/posts/WPA_woes/index.html13
-rw-r--r--docs/posts/a_reference_counted_afterlife/index.html40
-rw-r--r--docs/posts/auto-currying_rust_functions/index.html496
-rw-r--r--docs/posts/bash_harder_with_vim/index.html22
-rw-r--r--docs/posts/bye_bye_BDFs/index.html28
-rw-r--r--docs/posts/call_to_ARMs/index.html49
-rw-r--r--docs/posts/color_conundrum/index.html36
-rw-r--r--docs/posts/curing_a_case_of_git-UX/index.html180
-rw-r--r--docs/posts/font_size_fallacies/index.html79
-rw-r--r--docs/posts/get_better_at_yanking_and_putting_in_vim/index.html5
-rw-r--r--docs/posts/gripes_with_go/index.html85
-rw-r--r--docs/posts/hold_position!/index.html20
-rw-r--r--docs/posts/index.html14
-rw-r--r--docs/posts/lightweight_linting/index.html212
-rw-r--r--docs/posts/lotus58/index.html83
-rw-r--r--docs/posts/my_setup/index.html25
-rw-r--r--docs/posts/nixOS/index.html74
-rw-r--r--docs/posts/novice_nix:_flake_templates/index.html171
-rw-r--r--docs/posts/onivim_sucks/index.html39
-rw-r--r--docs/posts/pixel_art_in_GIMP/index.html101
-rw-r--r--docs/posts/programming_on_34_keys/index.html157
-rw-r--r--docs/posts/rapid_refactoring_with_vim/index.html119
-rw-r--r--docs/posts/self-hosting_git/index.html67
-rw-r--r--docs/posts/static_sites_with_bash/index.html36
-rw-r--r--docs/posts/termux_tandem/index.html29
-rw-r--r--docs/posts/turing_complete_type_systems/index.html24
31 files changed, 3632 insertions, 1245 deletions
diff --git a/docs/art/index.html b/docs/art/index.html
index 88c6cce..e3dee61 100644
--- a/docs/art/index.html
+++ b/docs/art/index.html
@@ -21,7 +21,18 @@
21 <h1>Art</h1> 21 <h1>Art</h1>
22 <div class="separator"></div> 22 <div class="separator"></div>
23 <div id="photos"> 23 <div id="photos">
24 <a href="/art/ferris.png"> 24 <a href="/art/lapse.png">
25 <div class="photo-container">
26 <img src="/art/lapse.png">
27 <div class="photo-overlay">
28 <div class="photo-text">
29 <div class="photo-title">Lapse</div>
30 <div class="photo-date">12/02 — 2023</div>
31 </div>
32 </div>
33 </div>
34</a>
35<a href="/art/ferris.png">
25 <div class="photo-container"> 36 <div class="photo-container">
26 <img src="/art/ferris.png"> 37 <img src="/art/ferris.png">
27 <div class="photo-overlay"> 38 <div class="photo-overlay">
@@ -43,23 +54,23 @@
43 </div> 54 </div>
44 </div> 55 </div>
45</a> 56</a>
46<a href="/art/the_sect.png"> 57<a href="/art/ivory_tower.png">
47 <div class="photo-container"> 58 <div class="photo-container">
48 <img src="/art/the_sect.png"> 59 <img src="/art/ivory_tower.png">
49 <div class="photo-overlay"> 60 <div class="photo-overlay">
50 <div class="photo-text"> 61 <div class="photo-text">
51 <div class="photo-title">The Sect</div> 62 <div class="photo-title">Ivory Tower</div>
52 <div class="photo-date">03/06 — 2022</div> 63 <div class="photo-date">03/06 — 2022</div>
53 </div> 64 </div>
54 </div> 65 </div>
55 </div> 66 </div>
56</a> 67</a>
57<a href="/art/ivory_tower.png"> 68<a href="/art/the_sect.png">
58 <div class="photo-container"> 69 <div class="photo-container">
59 <img src="/art/ivory_tower.png"> 70 <img src="/art/the_sect.png">
60 <div class="photo-overlay"> 71 <div class="photo-overlay">
61 <div class="photo-text"> 72 <div class="photo-text">
62 <div class="photo-title">Ivory Tower</div> 73 <div class="photo-title">The Sect</div>
63 <div class="photo-date">03/06 — 2022</div> 74 <div class="photo-date">03/06 — 2022</div>
64 </div> 75 </div>
65 </div> 76 </div>
@@ -241,23 +252,23 @@
241 </div> 252 </div>
242 </div> 253 </div>
243</a> 254</a>
244<a href="/art/beret_beaver.png"> 255<a href="/art/aviator_owl.png">
245 <div class="photo-container"> 256 <div class="photo-container">
246 <img src="/art/beret_beaver.png"> 257 <img src="/art/aviator_owl.png">
247 <div class="photo-overlay"> 258 <div class="photo-overlay">
248 <div class="photo-text"> 259 <div class="photo-text">
249 <div class="photo-title">Beret Beaver</div> 260 <div class="photo-title">Aviator Owl</div>
250 <div class="photo-date">01/05 — 2021</div> 261 <div class="photo-date">01/05 — 2021</div>
251 </div> 262 </div>
252 </div> 263 </div>
253 </div> 264 </div>
254</a> 265</a>
255<a href="/art/aviator_owl.png"> 266<a href="/art/beret_beaver.png">
256 <div class="photo-container"> 267 <div class="photo-container">
257 <img src="/art/aviator_owl.png"> 268 <img src="/art/beret_beaver.png">
258 <div class="photo-overlay"> 269 <div class="photo-overlay">
259 <div class="photo-text"> 270 <div class="photo-text">
260 <div class="photo-title">Aviator Owl</div> 271 <div class="photo-title">Beret Beaver</div>
261 <div class="photo-date">01/05 — 2021</div> 272 <div class="photo-date">01/05 — 2021</div>
262 </div> 273 </div>
263 </div> 274 </div>
diff --git a/docs/art/lapse.png b/docs/art/lapse.png
new file mode 100644
index 0000000..52b7721
--- /dev/null
+++ b/docs/art/lapse.png
Binary files differ
diff --git a/docs/index.html b/docs/index.html
index b3d6de9..9c74671 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -50,7 +50,7 @@
50 </td> 50 </td>
51 <td class=table-stats> 51 <td class=table-stats>
52 <span class="stats-number"> 52 <span class="stats-number">
53 9.5 53 9.6
54 </span> 54 </span>
55 <span class=stats-unit>min</span> 55 <span class=stats-unit>min</span>
56 </td> 56 </td>
@@ -78,15 +78,15 @@
78 <tr> 78 <tr>
79 <td class=table-post> 79 <td class=table-post>
80 <div class="date"> 80 <div class="date">
81 29/08 — 2022 81 12/02 — 2023
82 </div> 82 </div>
83 <a href="/art/ferris.png" class="post-link"> 83 <a href="/art/lapse.png" class="post-link">
84 <span class="post-link">Ferris</span> 84 <span class="post-link">Lapse</span>
85 </a> 85 </a>
86 </td> 86 </td>
87 <td class="table-stats"> 87 <td class="table-stats">
88 <a href="/art/ferris.png"> 88 <a href="/art/lapse.png">
89 <img src="/art/ferris.png" height="50px"> 89 <img src="/art/lapse.png" height="50px">
90 </a> 90 </a>
91 </td> 91 </td>
92 </tr> 92 </tr>
diff --git a/docs/index.xml b/docs/index.xml
index 5586abd..c0277f1 100644
--- a/docs/index.xml
+++ b/docs/index.xml
@@ -13,51 +13,86 @@
13 <copyright>Creative Commons BY-NC-SA 4.0</copyright> 13 <copyright>Creative Commons BY-NC-SA 4.0</copyright>
14 <item> 14 <item>
15<title>Curing A Case Of Git-UX</title> 15<title>Curing A Case Of Git-UX</title>
16<description>&lt;p&gt;Git worktrees are great, but they fall behind the venerable &lt;code&gt;git checkout&lt;/code&gt; sometimes. I attempted to fix that with &lt;a href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt; and a bit of bash.&lt;/p&gt; 16<description>&lt;p&gt;Git worktrees are great, but they fall behind the venerable
17&lt;p&gt;&lt;a href="https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps"&gt;&lt;img src="https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps.svg" /&gt;&lt;/a&gt;&lt;/p&gt; 17&lt;code&gt;git checkout&lt;/code&gt; sometimes. I attempted to fix that with &lt;a
18&lt;p&gt;Fear not if you haven’t heard of “worktrees”, I have included a primer here.&lt;br /&gt; 18href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt; and a bit of bash.&lt;/p&gt;
19&lt;p&gt;&lt;a href="https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps"&gt;&lt;img
20src="https://asciinema.org/a/D297ztKRzpE4gAHbPTPmkqYps.svg" /&gt;&lt;/a&gt;&lt;/p&gt;
21&lt;p&gt;Fear not if you haven’t heard of “worktrees”, I have included a
22primer here.&lt;br /&gt;
19&lt;a href="#what-makes-them-clunky"&gt;Skip the primer -&amp;gt;&lt;/a&gt;.&lt;/p&gt; 23&lt;a href="#what-makes-them-clunky"&gt;Skip the primer -&amp;gt;&lt;/a&gt;.&lt;/p&gt;
20&lt;h3 id="why-worktrees"&gt;Why Worktrees?&lt;/h3&gt; 24&lt;h3 id="why-worktrees"&gt;Why Worktrees?&lt;/h3&gt;
21&lt;p&gt;Picture this. You are whacking away on a feature branch. Halfway there, in fact. Your friend asks you fix something urgently. You proceed to do one of three things:&lt;/p&gt; 25&lt;p&gt;Picture this. You are whacking away on a feature branch. Halfway
26there, in fact. Your friend asks you fix something urgently. You proceed
27to do one of three things:&lt;/p&gt;
22&lt;ul&gt; 28&lt;ul&gt;
23&lt;li&gt;create a temporary branch, make a WIP commit, begin working on the fix&lt;/li&gt; 29&lt;li&gt;create a temporary branch, make a WIP commit, begin working on the
30fix&lt;/li&gt;
24&lt;li&gt;stash away your changes, begin working on the fix&lt;/li&gt; 31&lt;li&gt;stash away your changes, begin working on the fix&lt;/li&gt;
25&lt;li&gt;unfriend said friend for disturbing your flow&lt;/li&gt; 32&lt;li&gt;unfriend said friend for disturbing your flow&lt;/li&gt;
26&lt;/ul&gt; 33&lt;/ul&gt;
27&lt;p&gt;All of these options are … subpar. With the temporary branch, you are forced to create a partial, non-working commit, and then reset said commit once done with the fix. With the stash approach, you are required to now keep a mental model of the stash, be aware of untracked files that don’t get stashed by default, etc. Why won’t git just let you work on two things at the same time without &lt;em&gt;thinking&lt;/em&gt; so much?&lt;/p&gt; 34&lt;p&gt;All of these options are … subpar. With the temporary branch, you are
28&lt;p&gt;That is exactly what worktrees let you do. Worktrees let you have more than one checkout at a time, each checkout in a separate directory. Like creating a new clone, but safer (it disallows checking out the same branch twice) and a lot more space efficient (the new working tree is “linked” to the “main” worktree, and a good amount of stuff is shared). When your friend asks you to make the fix, you proceed like so:&lt;/p&gt; 35forced to create a partial, non-working commit, and then reset said
36commit once done with the fix. With the stash approach, you are required
37to now keep a mental model of the stash, be aware of untracked files
38that don’t get stashed by default, etc. Why won’t git just let you work
39on two things at the same time without &lt;em&gt;thinking&lt;/em&gt; so much?&lt;/p&gt;
40&lt;p&gt;That is exactly what worktrees let you do. Worktrees let you have
41more than one checkout at a time, each checkout in a separate directory.
42Like creating a new clone, but safer (it disallows checking out the same
43branch twice) and a lot more space efficient (the new working tree is
44“linked” to the “main” worktree, and a good amount of stuff is shared).
45When your friend asks you to make the fix, you proceed like so:&lt;/p&gt;
29&lt;ol type="1"&gt; 46&lt;ol type="1"&gt;
30&lt;li&gt;Create a new working tree with:&lt;/li&gt; 47&lt;li&gt;Create a new working tree with:&lt;/li&gt;
31&lt;/ol&gt; 48&lt;/ol&gt;
32&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# git worktree add -b &amp;lt;branch-name&amp;gt; &amp;lt;path&amp;gt; &amp;lt;from&amp;gt;&lt;/span&gt;&lt;/span&gt; 49&lt;div class="sourceCode" id="cb1"&gt;&lt;pre
50class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# git worktree add -b &amp;lt;branch-name&amp;gt; &amp;lt;path&amp;gt; &amp;lt;from&amp;gt;&lt;/span&gt;&lt;/span&gt;
33&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;git&lt;/span&gt; worktree add &lt;span class="at"&gt;-b&lt;/span&gt; fix-stuff /path/to/tree master&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 51&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;git&lt;/span&gt; worktree add &lt;span class="at"&gt;-b&lt;/span&gt; fix-stuff /path/to/tree master&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
34&lt;ol start="2" type="1"&gt; 52&lt;ol start="2" type="1"&gt;
35&lt;li&gt;&lt;code&gt;cd&lt;/code&gt; into &lt;code&gt;/path/to/tree&lt;/code&gt;&lt;/li&gt; 53&lt;li&gt;&lt;code&gt;cd&lt;/code&gt; into &lt;code&gt;/path/to/tree&lt;/code&gt;&lt;/li&gt;
36&lt;li&gt;Fix, test, commit, push, party&lt;/li&gt; 54&lt;li&gt;Fix, test, commit, push, party&lt;/li&gt;
37&lt;li&gt;Go back to your work, &lt;code&gt;cd -&lt;/code&gt;&lt;/li&gt; 55&lt;li&gt;Go back to your work, &lt;code&gt;cd -&lt;/code&gt;&lt;/li&gt;
38&lt;/ol&gt; 56&lt;/ol&gt;
39&lt;p&gt;Easy as cake. You didn’t have to settle for a partially working commit, you didn’t to deal with this “stash” thing, &lt;em&gt;and&lt;/em&gt; you didn’t have to unfriend your friend. Treating each branch as a directory just &lt;em&gt;feels&lt;/em&gt; more intuitive, more UNIX-y.&lt;/p&gt; 57&lt;p&gt;Easy as cake. You didn’t have to settle for a partially working
40&lt;p&gt;A few weeks later, you find yourself singing in praise of worktrees, working on several things simultaneously. And at the same time, cursing them for being a little … clunky.&lt;/p&gt; 58commit, you didn’t to deal with this “stash” thing, &lt;em&gt;and&lt;/em&gt; you
59didn’t have to unfriend your friend. Treating each branch as a directory
60just &lt;em&gt;feels&lt;/em&gt; more intuitive, more UNIX-y.&lt;/p&gt;
61&lt;p&gt;A few weeks later, you find yourself singing in praise of worktrees,
62working on several things simultaneously. And at the same time, cursing
63them for being a little … clunky.&lt;/p&gt;
41&lt;h3 id="what-makes-them-clunky"&gt;What makes them clunky?&lt;/h3&gt; 64&lt;h3 id="what-makes-them-clunky"&gt;What makes them clunky?&lt;/h3&gt;
42&lt;p&gt;Worktrees are great at what they claim to do. They stay out of the way when you need a checkout posthaste. However, as you start using them regularly, you realize they are not as flexible as &lt;code&gt;git checkout&lt;/code&gt; or &lt;code&gt;git switch&lt;/code&gt;.&lt;/p&gt; 65&lt;p&gt;Worktrees are great at what they claim to do. They stay out of the
66way when you need a checkout posthaste. However, as you start using them
67regularly, you realize they are not as flexible as
68&lt;code&gt;git checkout&lt;/code&gt; or &lt;code&gt;git switch&lt;/code&gt;.&lt;/p&gt;
43&lt;h4 id="branch-hopping"&gt;Branch-hopping&lt;/h4&gt; 69&lt;h4 id="branch-hopping"&gt;Branch-hopping&lt;/h4&gt;
44&lt;p&gt;You can &lt;code&gt;git checkout &amp;lt;branch&amp;gt;&lt;/code&gt; from anywhere within a git repository. You can’t “jump” to a worktree in the same fashion. The closest you can get, is to run &lt;code&gt;git worktree list&lt;/code&gt;, copy the path corresponding to your branch, and &lt;code&gt;cd&lt;/code&gt; into it.&lt;/p&gt; 70&lt;p&gt;You can &lt;code&gt;git checkout &amp;lt;branch&amp;gt;&lt;/code&gt; from anywhere within
71a git repository. You can’t “jump” to a worktree in the same fashion.
72The closest you can get, is to run &lt;code&gt;git worktree list&lt;/code&gt;, copy
73the path corresponding to your branch, and &lt;code&gt;cd&lt;/code&gt; into it.&lt;/p&gt;
45&lt;p&gt;Branch-hopping with the good ol’ git-checkout:&lt;/p&gt; 74&lt;p&gt;Branch-hopping with the good ol’ git-checkout:&lt;/p&gt;
46&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# anywhere, anytime&lt;/span&gt;&lt;/span&gt; 75&lt;div class="sourceCode" id="cb2"&gt;&lt;pre
76class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# anywhere, anytime&lt;/span&gt;&lt;/span&gt;
47&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout feature/is-ascii-octdigit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 77&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout feature/is-ascii-octdigit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
48&lt;p&gt;Meanwhile, in worktree world:&lt;/p&gt; 78&lt;p&gt;Meanwhile, in worktree world:&lt;/p&gt;
49&lt;div class="sourceCode" id="cb3"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# keeping these paths in your head is hard&lt;/span&gt;&lt;/span&gt; 79&lt;div class="sourceCode" id="cb3"&gt;&lt;pre
80class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# keeping these paths in your head is hard&lt;/span&gt;&lt;/span&gt;
50&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git worktree list&lt;/span&gt; 81&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git worktree list&lt;/span&gt;
51&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/master&lt;/span&gt; eac6c33bc63 [master]&lt;/span&gt; 82&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/master&lt;/span&gt; eac6c33bc63 &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;master&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
52&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/improve-std-char-docs&lt;/span&gt; 94cba88553e [improve-std-char-docs]&lt;/span&gt; 83&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/improve-std-char-docs&lt;/span&gt; 94cba88553e &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;improve&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;std&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;char&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;docs&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
53&lt;span id="cb3-5"&gt;&lt;a href="#cb3-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt; bc57be3af7a [feature/is-ascii-octdigit]&lt;/span&gt; 84&lt;span id="cb3-5"&gt;&lt;a href="#cb3-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt; bc57be3af7a &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;feature/is&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ascii&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;octdigit&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
54&lt;span id="cb3-6"&gt;&lt;a href="#cb3-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/my/other/path/oh/god&lt;/span&gt; op57or3ns7n [fix/some-error]&lt;/span&gt; 85&lt;span id="cb3-6"&gt;&lt;a href="#cb3-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/my/other/path/oh/god&lt;/span&gt; op57or3ns7n &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;fix/some&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;error&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
55&lt;span id="cb3-7"&gt;&lt;a href="#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 86&lt;span id="cb3-7"&gt;&lt;a href="#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
56&lt;span id="cb3-8"&gt;&lt;a href="#cb3-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; cd ~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 87&lt;span id="cb3-8"&gt;&lt;a href="#cb3-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; cd ~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
57&lt;h4 id="branch-previewing"&gt;Branch-previewing&lt;/h4&gt; 88&lt;h4 id="branch-previewing"&gt;Branch-previewing&lt;/h4&gt;
58&lt;p&gt;You can “preview” branches with &lt;code&gt;git branch -v&lt;/code&gt;. However, to get an idea of what “recent activity” on a worktree looks like, you might need some juggling. You can’t glean much info about a worktree in a jiffy.&lt;/p&gt; 89&lt;p&gt;You can “preview” branches with &lt;code&gt;git branch -v&lt;/code&gt;. However,
90to get an idea of what “recent activity” on a worktree looks like, you
91might need some juggling. You can’t glean much info about a worktree in
92a jiffy.&lt;/p&gt;
59&lt;p&gt;Branch-previewing with the good ol’ git-branch:&lt;/p&gt; 93&lt;p&gt;Branch-previewing with the good ol’ git-branch:&lt;/p&gt;
60&lt;div class="sourceCode" id="cb4"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git branch &lt;span class="at"&gt;-v&lt;/span&gt;&lt;/span&gt; 94&lt;div class="sourceCode" id="cb4"&gt;&lt;pre
95class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git branch &lt;span class="at"&gt;-v&lt;/span&gt;&lt;/span&gt;
61&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; feature/is-ascii-octdigit bc57be3af7a introduce {char, u8}::is_ ...&lt;/span&gt; 96&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; feature/is-ascii-octdigit bc57be3af7a introduce {char, u8}::is_ ...&lt;/span&gt;
62&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; improve-std-char-docs 94cba88553e add whitespace in assert ...&lt;/span&gt; 97&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; improve-std-char-docs 94cba88553e add whitespace in assert ...&lt;/span&gt;
63&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;*&lt;/span&gt; master eac6c33bc63 Auto merge of &lt;span class="co"&gt;#100869 - n ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 98&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;*&lt;/span&gt; master eac6c33bc63 Auto merge of &lt;span class="co"&gt;#100869 - n ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
@@ -76,41 +111,60 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
76 111
77# extra work to make the branch &amp;lt;-&amp;gt; worktree correspondence&lt;/code&gt;&lt;/pre&gt; 112# extra work to make the branch &amp;lt;-&amp;gt; worktree correspondence&lt;/code&gt;&lt;/pre&gt;
78&lt;h4 id="shell-completions"&gt;Shell completions&lt;/h4&gt; 113&lt;h4 id="shell-completions"&gt;Shell completions&lt;/h4&gt;
79&lt;p&gt;Lastly, you can bank on shell completions to fill in your branch whilst using &lt;code&gt;git checkout&lt;/code&gt;. Worktrees have no such conveniences.&lt;/p&gt; 114&lt;p&gt;Lastly, you can bank on shell completions to fill in your branch
115whilst using &lt;code&gt;git checkout&lt;/code&gt;. Worktrees have no such
116conveniences.&lt;/p&gt;
80&lt;p&gt;We can mend these minor faults with fzf.&lt;/p&gt; 117&lt;p&gt;We can mend these minor faults with fzf.&lt;/p&gt;
81&lt;h3 id="unclunkifying-worktrees"&gt;Unclunkifying worktrees&lt;/h3&gt; 118&lt;h3 id="unclunkifying-worktrees"&gt;Unclunkifying worktrees&lt;/h3&gt;
82&lt;p&gt;I’d suggest looking up &lt;a href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt; (or &lt;a href="https://github.com/lotabout/skim"&gt;skim&lt;/a&gt; or &lt;a href="https://github.com/jhawthorn/fzy"&gt;fzy&lt;/a&gt;). These things make it cake-easy to add interactivity to your shell. Onto fixing the first minor fault, the inability to “jump” to a worktree from anywhere within a git repository.&lt;/p&gt; 119&lt;p&gt;I’d suggest looking up &lt;a
83&lt;p&gt;I have a little function called &lt;code&gt;gwj&lt;/code&gt; which stands for “git worktree jump”. The idea is to list all the worktrees, select one with fzf, and &lt;code&gt;cd&lt;/code&gt; to it upon selection:&lt;/p&gt; 120href="https://github.com/junegunn/fzf"&gt;fzf&lt;/a&gt; (or &lt;a
84&lt;div class="sourceCode" id="cb6"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb6-1"&gt;&lt;a href="#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;gwj ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt; 121href="https://github.com/lotabout/skim"&gt;skim&lt;/a&gt; or &lt;a
122href="https://github.com/jhawthorn/fzy"&gt;fzy&lt;/a&gt;). These things make it
123cake-easy to add interactivity to your shell. Onto fixing the first
124minor fault, the inability to “jump” to a worktree from anywhere within
125a git repository.&lt;/p&gt;
126&lt;p&gt;I have a little function called &lt;code&gt;gwj&lt;/code&gt; which stands for
127“git worktree jump”. The idea is to list all the worktrees, select one
128with fzf, and &lt;code&gt;cd&lt;/code&gt; to it upon selection:&lt;/p&gt;
129&lt;div class="sourceCode" id="cb6"&gt;&lt;pre
130class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb6-1"&gt;&lt;a href="#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;gwj ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt;
85&lt;span id="cb6-2"&gt;&lt;a href="#cb6-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;local&lt;/span&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;/span&gt; 131&lt;span id="cb6-2"&gt;&lt;a href="#cb6-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;local&lt;/span&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;/span&gt;
86&lt;span id="cb6-3"&gt;&lt;a href="#cb6-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;$(&lt;/span&gt;&lt;span class="fu"&gt;git&lt;/span&gt; worktree list &lt;span class="kw"&gt;|&lt;/span&gt; &lt;span class="ex"&gt;fzf&lt;/span&gt; &lt;span class="kw"&gt;|&lt;/span&gt; &lt;span class="fu"&gt;awk&lt;/span&gt; &lt;span class="st"&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt;&lt;span class="va"&gt;)&lt;/span&gt;&lt;/span&gt; 132&lt;span id="cb6-3"&gt;&lt;a href="#cb6-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;$(&lt;/span&gt;&lt;span class="fu"&gt;git&lt;/span&gt; worktree list &lt;span class="kw"&gt;|&lt;/span&gt; &lt;span class="ex"&gt;fzf&lt;/span&gt; &lt;span class="kw"&gt;|&lt;/span&gt; &lt;span class="fu"&gt;awk&lt;/span&gt; &lt;span class="st"&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt;&lt;span class="va"&gt;)&lt;/span&gt;&lt;/span&gt;
87&lt;span id="cb6-4"&gt;&lt;a href="#cb6-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="va"&gt;$out&lt;/span&gt;&lt;/span&gt; 133&lt;span id="cb6-4"&gt;&lt;a href="#cb6-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="va"&gt;$out&lt;/span&gt;&lt;/span&gt;
88&lt;span id="cb6-5"&gt;&lt;a href="#cb6-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 134&lt;span id="cb6-5"&gt;&lt;a href="#cb6-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
89&lt;p&gt;That is all of it really. Head into a git repository:&lt;/p&gt; 135&lt;p&gt;That is all of it really. Head into a git repository:&lt;/p&gt;
90&lt;div class="sourceCode" id="cb7"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb7-1"&gt;&lt;a href="#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# here, &amp;quot;master&amp;quot; is a directory, which contains my main&lt;/span&gt;&lt;/span&gt; 136&lt;div class="sourceCode" id="cb7"&gt;&lt;pre
137class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb7-1"&gt;&lt;a href="#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# here, &amp;quot;master&amp;quot; is a directory, which contains my main&lt;/span&gt;&lt;/span&gt;
91&lt;span id="cb7-2"&gt;&lt;a href="#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# worktree: a checkout of the master branch on rust-lang/rust &lt;/span&gt;&lt;/span&gt; 138&lt;span id="cb7-2"&gt;&lt;a href="#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# worktree: a checkout of the master branch on rust-lang/rust &lt;/span&gt;&lt;/span&gt;
92&lt;span id="cb7-3"&gt;&lt;a href="#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; cd ~/worktrees/rustc/master/library/core/src&lt;/span&gt; 139&lt;span id="cb7-3"&gt;&lt;a href="#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; cd ~/worktrees/rustc/master/library/core/src&lt;/span&gt;
93&lt;span id="cb7-4"&gt;&lt;a href="#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; &lt;span class="co"&gt;# hack away&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 140&lt;span id="cb7-4"&gt;&lt;a href="#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; &lt;span class="co"&gt;# hack away&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
94&lt;p&gt;Preferably one with a few worktrees:&lt;/p&gt; 141&lt;p&gt;Preferably one with a few worktrees:&lt;/p&gt;
95&lt;div class="sourceCode" id="cb8"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb8-1"&gt;&lt;a href="#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git worktree list&lt;/span&gt; 142&lt;div class="sourceCode" id="cb8"&gt;&lt;pre
96&lt;span id="cb8-2"&gt;&lt;a href="#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/master&lt;/span&gt; eac6c33bc63 [master]&lt;/span&gt; 143class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb8-1"&gt;&lt;a href="#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git worktree list&lt;/span&gt;
97&lt;span id="cb8-3"&gt;&lt;a href="#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/improve-std-char-docs&lt;/span&gt; 94cba88553e [improve-std-char-docs]&lt;/span&gt; 144&lt;span id="cb8-2"&gt;&lt;a href="#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/master&lt;/span&gt; eac6c33bc63 &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;master&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
98&lt;span id="cb8-4"&gt;&lt;a href="#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt; bc57be3af7a [feature/is-ascii-octdigit]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 145&lt;span id="cb8-3"&gt;&lt;a href="#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/improve-std-char-docs&lt;/span&gt; 94cba88553e &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;improve&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;std&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;char&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;docs&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
99&lt;p&gt;And hit &lt;code&gt;gwj&lt;/code&gt; (pretend that the pipe, |, is your cursor):&lt;/p&gt; 146&lt;span id="cb8-4"&gt;&lt;a href="#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt; bc57be3af7a &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;feature/is&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ascii&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;octdigit&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
100&lt;div class="sourceCode" id="cb9"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb9-1"&gt;&lt;a href="#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj&lt;/span&gt; 147&lt;p&gt;And hit &lt;code&gt;gwj&lt;/code&gt; (pretend that the pipe, |, is your
148cursor):&lt;/p&gt;
149&lt;div class="sourceCode" id="cb9"&gt;&lt;pre
150class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb9-1"&gt;&lt;a href="#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj&lt;/span&gt;
101&lt;span id="cb9-2"&gt;&lt;a href="#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kw"&gt;|&lt;/span&gt;&lt;/span&gt; 151&lt;span id="cb9-2"&gt;&lt;a href="#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kw"&gt;|&lt;/span&gt;&lt;/span&gt;
102&lt;span id="cb9-3"&gt;&lt;a href="#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;4/4&lt;/span&gt;&lt;/span&gt; 152&lt;span id="cb9-3"&gt;&lt;a href="#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;4/4&lt;/span&gt;&lt;/span&gt;
103&lt;span id="cb9-4"&gt;&lt;a href="#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; ~/worktrees/rustc/master &lt;span class="ex"&gt;eac6c33bc63&lt;/span&gt; [master]&lt;/span&gt; 153&lt;span id="cb9-4"&gt;&lt;a href="#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; ~/worktrees/rustc/master &lt;span class="ex"&gt;eac6c33bc63&lt;/span&gt; &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;master&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
104&lt;span id="cb9-5"&gt;&lt;a href="#cb9-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;~/worktrees/rustc/improve-std-char-docs&lt;/span&gt; 94cba88553e [improve-std-char-docs]&lt;/span&gt; 154&lt;span id="cb9-5"&gt;&lt;a href="#cb9-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;~/worktrees/rustc/improve-std-char-docs&lt;/span&gt; 94cba88553e &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;improve&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;std&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;char&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;docs&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;
105&lt;span id="cb9-6"&gt;&lt;a href="#cb9-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt; bc57be3af7a [feature/is-ascii-octdigit]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 155&lt;span id="cb9-6"&gt;&lt;a href="#cb9-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;~/worktrees/rustc/is-ascii-octdigit&lt;/span&gt; bc57be3af7a &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;feature/is&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ascii&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;octdigit&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
106&lt;p&gt;Approximately type in your branch of choice:&lt;/p&gt; 156&lt;p&gt;Approximately type in your branch of choice:&lt;/p&gt;
107&lt;div class="sourceCode" id="cb10"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb10-1"&gt;&lt;a href="#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj&lt;/span&gt; 157&lt;div class="sourceCode" id="cb10"&gt;&lt;pre
158class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb10-1"&gt;&lt;a href="#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj&lt;/span&gt;
108&lt;span id="cb10-2"&gt;&lt;a href="#cb10-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; docs&lt;span class="kw"&gt;|&lt;/span&gt;&lt;/span&gt; 159&lt;span id="cb10-2"&gt;&lt;a href="#cb10-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; docs&lt;span class="kw"&gt;|&lt;/span&gt;&lt;/span&gt;
109&lt;span id="cb10-3"&gt;&lt;a href="#cb10-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;4/4&lt;/span&gt;&lt;/span&gt; 160&lt;span id="cb10-3"&gt;&lt;a href="#cb10-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;4/4&lt;/span&gt;&lt;/span&gt;
110&lt;span id="cb10-4"&gt;&lt;a href="#cb10-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; ~/worktrees/rustc/improve-std-char-docs &lt;span class="ex"&gt;94cba88553e&lt;/span&gt; [improve-std-char-docs]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 161&lt;span id="cb10-4"&gt;&lt;a href="#cb10-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; ~/worktrees/rustc/improve-std-char-docs &lt;span class="ex"&gt;94cba88553e&lt;/span&gt; &lt;span class="pp"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;improve&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;std&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;char&lt;/span&gt;&lt;span class="pp"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;docs&lt;/span&gt;&lt;span class="pp"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
111&lt;p&gt;And hit enter. You should find yourself in the selected worktree.&lt;/p&gt; 162&lt;p&gt;And hit enter. You should find yourself in the selected worktree.&lt;/p&gt;
112&lt;p&gt;Onward, to the next fault, lack of preview-bility. We can utilize fzf’s aptly named &lt;code&gt;--preview&lt;/code&gt; flag, to, well, preview our worktree before performing a selection:&lt;/p&gt; 163&lt;p&gt;Onward, to the next fault, lack of preview-bility. We can utilize
113&lt;div class="sourceCode" id="cb11"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb11-1"&gt;&lt;a href="#cb11-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;gwj ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt; 164fzf’s aptly named &lt;code&gt;--preview&lt;/code&gt; flag, to, well, preview our
165worktree before performing a selection:&lt;/p&gt;
166&lt;div class="sourceCode" id="cb11"&gt;&lt;pre
167class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb11-1"&gt;&lt;a href="#cb11-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;gwj ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt;
114&lt;span id="cb11-2"&gt;&lt;a href="#cb11-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;local&lt;/span&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;/span&gt; 168&lt;span id="cb11-2"&gt;&lt;a href="#cb11-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;local&lt;/span&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;/span&gt;
115&lt;span id="cb11-3"&gt;&lt;a href="#cb11-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;$(&lt;/span&gt;&lt;/span&gt; 169&lt;span id="cb11-3"&gt;&lt;a href="#cb11-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;$(&lt;/span&gt;&lt;/span&gt;
116&lt;span id="cb11-4"&gt;&lt;a href="#cb11-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="fu"&gt;git&lt;/span&gt; worktree list &lt;span class="kw"&gt;|&lt;/span&gt;&lt;/span&gt; 170&lt;span id="cb11-4"&gt;&lt;a href="#cb11-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="fu"&gt;git&lt;/span&gt; worktree list &lt;span class="kw"&gt;|&lt;/span&gt;&lt;/span&gt;
@@ -119,8 +173,10 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
119&lt;span id="cb11-7"&gt;&lt;a href="#cb11-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;)&lt;/span&gt;&lt;/span&gt; 173&lt;span id="cb11-7"&gt;&lt;a href="#cb11-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;)&lt;/span&gt;&lt;/span&gt;
120&lt;span id="cb11-8"&gt;&lt;a href="#cb11-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="va"&gt;$out&lt;/span&gt;&lt;/span&gt; 174&lt;span id="cb11-8"&gt;&lt;a href="#cb11-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="va"&gt;$out&lt;/span&gt;&lt;/span&gt;
121&lt;span id="cb11-9"&gt;&lt;a href="#cb11-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 175&lt;span id="cb11-9"&gt;&lt;a href="#cb11-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
122&lt;p&gt;Once again, hit &lt;code&gt;gwj&lt;/code&gt; inside a git repository with linked worktrees:&lt;/p&gt; 176&lt;p&gt;Once again, hit &lt;code&gt;gwj&lt;/code&gt; inside a git repository with linked
123&lt;div class="sourceCode" id="cb12"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb12-1"&gt;&lt;a href="#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj&lt;/span&gt; 177worktrees:&lt;/p&gt;
178&lt;div class="sourceCode" id="cb12"&gt;&lt;pre
179class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb12-1"&gt;&lt;a href="#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj&lt;/span&gt;
124&lt;span id="cb12-2"&gt;&lt;a href="#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;╭─────────────────────────────────────────────────────────╮&lt;/span&gt;&lt;/span&gt; 180&lt;span id="cb12-2"&gt;&lt;a href="#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;╭─────────────────────────────────────────────────────────╮&lt;/span&gt;&lt;/span&gt;
125&lt;span id="cb12-3"&gt;&lt;a href="#cb12-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; eac6c33bc63 Auto merge of 100869 nnethercote:replace... │&lt;/span&gt; 181&lt;span id="cb12-3"&gt;&lt;a href="#cb12-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; eac6c33bc63 Auto merge of 100869 nnethercote:replace... │&lt;/span&gt;
126&lt;span id="cb12-4"&gt;&lt;a href="#cb12-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │&lt;/span&gt; 182&lt;span id="cb12-4"&gt;&lt;a href="#cb12-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;│&lt;/span&gt; b32223fec10 Auto merge of 100707 dzvon:fix-typo, r=d... │&lt;/span&gt;
@@ -138,9 +194,16 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
138&lt;span id="cb12-16"&gt;&lt;a href="#cb12-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; /home/np/worktrees/compiler/master &lt;span class="ex"&gt;eac6c...&lt;/span&gt;&lt;/span&gt; 194&lt;span id="cb12-16"&gt;&lt;a href="#cb12-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; /home/np/worktrees/compiler/master &lt;span class="ex"&gt;eac6c...&lt;/span&gt;&lt;/span&gt;
139&lt;span id="cb12-17"&gt;&lt;a href="#cb12-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;/home/np/worktrees/compiler/improve-std-char-docs&lt;/span&gt; 94cba...&lt;/span&gt; 195&lt;span id="cb12-17"&gt;&lt;a href="#cb12-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;/home/np/worktrees/compiler/improve-std-char-docs&lt;/span&gt; 94cba...&lt;/span&gt;
140&lt;span id="cb12-18"&gt;&lt;a href="#cb12-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;/home/np/worktrees/compiler/is-ascii-octdigit&lt;/span&gt; bc57b...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 196&lt;span id="cb12-18"&gt;&lt;a href="#cb12-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;/home/np/worktrees/compiler/is-ascii-octdigit&lt;/span&gt; bc57b...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
141&lt;p&gt;A fancy preview of the last 10 commits on the branch that the selected worktree corresponds to. In other words, sight for sore eyes. Our little script is already shaping up to be useful, you hit &lt;code&gt;gwj&lt;/code&gt;, browse through your worktrees, preview each one and automatically &lt;code&gt;cd&lt;/code&gt; to your selection. But we are not done yet.&lt;/p&gt; 197&lt;p&gt;A fancy preview of the last 10 commits on the branch that the
142&lt;p&gt;The last fault was lack shell completions. A quick review of what a shell completion really does:&lt;/p&gt; 198selected worktree corresponds to. In other words, sight for sore eyes.
143&lt;div class="sourceCode" id="cb13"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb13-1"&gt;&lt;a href="#cb13-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout f&lt;span class="op"&gt;&amp;lt;&lt;/span&gt;tab&lt;span class="op"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt; 199Our little script is already shaping up to be useful, you hit
200&lt;code&gt;gwj&lt;/code&gt;, browse through your worktrees, preview each one and
201automatically &lt;code&gt;cd&lt;/code&gt; to your selection. But we are not done
202yet.&lt;/p&gt;
203&lt;p&gt;The last fault was lack shell completions. A quick review of what a
204shell completion really does:&lt;/p&gt;
205&lt;div class="sourceCode" id="cb13"&gt;&lt;pre
206class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb13-1"&gt;&lt;a href="#cb13-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout f&lt;span class="op"&gt;&amp;lt;&lt;/span&gt;tab&lt;span class="op"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
144&lt;span id="cb13-2"&gt;&lt;a href="#cb13-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;feature/is-ascii-octdigit&lt;/span&gt;&lt;/span&gt; 207&lt;span id="cb13-2"&gt;&lt;a href="#cb13-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;feature/is-ascii-octdigit&lt;/span&gt;&lt;/span&gt;
145&lt;span id="cb13-3"&gt;&lt;a href="#cb13-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;fix/some-error&lt;/span&gt;&lt;/span&gt; 208&lt;span id="cb13-3"&gt;&lt;a href="#cb13-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;fix/some-error&lt;/span&gt;&lt;/span&gt;
146&lt;span id="cb13-4"&gt;&lt;a href="#cb13-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;format-doc-tests&lt;/span&gt;&lt;/span&gt; 209&lt;span id="cb13-4"&gt;&lt;a href="#cb13-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;format-doc-tests&lt;/span&gt;&lt;/span&gt;
@@ -148,16 +211,22 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
148&lt;span id="cb13-6"&gt;&lt;a href="#cb13-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout feat&lt;span class="op"&gt;&amp;lt;&lt;/span&gt;tab&lt;span class="op"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt; 211&lt;span id="cb13-6"&gt;&lt;a href="#cb13-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout feat&lt;span class="op"&gt;&amp;lt;&lt;/span&gt;tab&lt;span class="op"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
149&lt;span id="cb13-7"&gt;&lt;a href="#cb13-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 212&lt;span id="cb13-7"&gt;&lt;a href="#cb13-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
150&lt;span id="cb13-8"&gt;&lt;a href="#cb13-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout feature/is-ascii-octdigit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 213&lt;span id="cb13-8"&gt;&lt;a href="#cb13-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; git checkout feature/is-ascii-octdigit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
151&lt;p&gt;Each time you hit “tab”, the shell produces a few “completion candidates”, and once you have just a single candidate left, the shell inserts that for you directly into your edit line. Of course, this process varies from shell to shell.&lt;/p&gt; 214&lt;p&gt;Each time you hit “tab”, the shell produces a few “completion
152&lt;p&gt;fzf narrows down your options as you type into the prompt, but you still have to:&lt;/p&gt; 215candidates”, and once you have just a single candidate left, the shell
216inserts that for you directly into your edit line. Of course, this
217process varies from shell to shell.&lt;/p&gt;
218&lt;p&gt;fzf narrows down your options as you type into the prompt, but you
219still have to:&lt;/p&gt;
153&lt;ol type="1"&gt; 220&lt;ol type="1"&gt;
154&lt;li&gt;Type &lt;code&gt;gwj&lt;/code&gt;&lt;/li&gt; 221&lt;li&gt;Type &lt;code&gt;gwj&lt;/code&gt;&lt;/li&gt;
155&lt;li&gt;Hit enter&lt;/li&gt; 222&lt;li&gt;Hit enter&lt;/li&gt;
156&lt;li&gt;Type out a query and narrow down your search&lt;/li&gt; 223&lt;li&gt;Type out a query and narrow down your search&lt;/li&gt;
157&lt;li&gt;Hit enter&lt;/li&gt; 224&lt;li&gt;Hit enter&lt;/li&gt;
158&lt;/ol&gt; 225&lt;/ol&gt;
159&lt;p&gt;We can speed that up a bit, have fzf narrow down the candidates on startup, just like our shell does:&lt;/p&gt; 226&lt;p&gt;We can speed that up a bit, have fzf narrow down the candidates on
160&lt;div class="sourceCode" id="cb14"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb14-1"&gt;&lt;a href="#cb14-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;gwj ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt; 227startup, just like our shell does:&lt;/p&gt;
228&lt;div class="sourceCode" id="cb14"&gt;&lt;pre
229class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb14-1"&gt;&lt;a href="#cb14-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;gwj ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt;
161&lt;span id="cb14-2"&gt;&lt;a href="#cb14-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;local&lt;/span&gt; &lt;span class="va"&gt;out&lt;/span&gt; &lt;span class="va"&gt;query&lt;/span&gt;&lt;/span&gt; 230&lt;span id="cb14-2"&gt;&lt;a href="#cb14-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;local&lt;/span&gt; &lt;span class="va"&gt;out&lt;/span&gt; &lt;span class="va"&gt;query&lt;/span&gt;&lt;/span&gt;
162&lt;span id="cb14-3"&gt;&lt;a href="#cb14-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;query&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;span class="va"&gt;${1&lt;/span&gt;&lt;span class="op"&gt;:-&lt;/span&gt; &lt;span class="va"&gt;}&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; 231&lt;span id="cb14-3"&gt;&lt;a href="#cb14-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;query&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;span class="va"&gt;${1&lt;/span&gt;&lt;span class="op"&gt;:-&lt;/span&gt; &lt;span class="va"&gt;}&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
163&lt;span id="cb14-4"&gt;&lt;a href="#cb14-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;$(&lt;/span&gt;&lt;/span&gt; 232&lt;span id="cb14-4"&gt;&lt;a href="#cb14-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;out&lt;/span&gt;&lt;span class="op"&gt;=&lt;/span&gt;&lt;span class="va"&gt;$(&lt;/span&gt;&lt;/span&gt;
@@ -167,8 +236,12 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
167&lt;span id="cb14-8"&gt;&lt;a href="#cb14-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;)&lt;/span&gt;&lt;/span&gt; 236&lt;span id="cb14-8"&gt;&lt;a href="#cb14-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;)&lt;/span&gt;&lt;/span&gt;
168&lt;span id="cb14-9"&gt;&lt;a href="#cb14-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="va"&gt;$out&lt;/span&gt;&lt;/span&gt; 237&lt;span id="cb14-9"&gt;&lt;a href="#cb14-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="va"&gt;$out&lt;/span&gt;&lt;/span&gt;
169&lt;span id="cb14-10"&gt;&lt;a href="#cb14-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 238&lt;span id="cb14-10"&gt;&lt;a href="#cb14-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
170&lt;p&gt;The change is extremely tiny, blink-and-you’ll-miss-it kinda tiny. We added a little &lt;code&gt;--query&lt;/code&gt; flag, that allows you to prefill the prompt, and the &lt;code&gt;-1&lt;/code&gt; flag, that avoids the interactive finder if only one match exists on startup:&lt;/p&gt; 239&lt;p&gt;The change is extremely tiny, blink-and-you’ll-miss-it kinda tiny. We
171&lt;div class="sourceCode" id="cb15"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb15-1"&gt;&lt;a href="#cb15-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# skip through the fzf prompt:&lt;/span&gt;&lt;/span&gt; 240added a little &lt;code&gt;--query&lt;/code&gt; flag, that allows you to prefill the
241prompt, and the &lt;code&gt;-1&lt;/code&gt; flag, that avoids the interactive finder
242if only one match exists on startup:&lt;/p&gt;
243&lt;div class="sourceCode" id="cb15"&gt;&lt;pre
244class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb15-1"&gt;&lt;a href="#cb15-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# skip through the fzf prompt:&lt;/span&gt;&lt;/span&gt;
172&lt;span id="cb15-2"&gt;&lt;a href="#cb15-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj master&lt;/span&gt; 245&lt;span id="cb15-2"&gt;&lt;a href="#cb15-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;λ&lt;/span&gt; gwj master&lt;/span&gt;
173&lt;span id="cb15-3"&gt;&lt;a href="#cb15-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# cd -- ~/worktrees/rustc/master&lt;/span&gt;&lt;/span&gt; 246&lt;span id="cb15-3"&gt;&lt;a href="#cb15-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# cd -- ~/worktrees/rustc/master&lt;/span&gt;&lt;/span&gt;
174&lt;span id="cb15-4"&gt;&lt;a href="#cb15-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 247&lt;span id="cb15-4"&gt;&lt;a href="#cb15-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
@@ -183,8 +256,11 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
183&lt;span id="cb15-13"&gt;&lt;a href="#cb15-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;2/2&lt;/span&gt;&lt;/span&gt; 256&lt;span id="cb15-13"&gt;&lt;a href="#cb15-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;2/2&lt;/span&gt;&lt;/span&gt;
184&lt;span id="cb15-14"&gt;&lt;a href="#cb15-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; /home/np/worktrees/compiler/improve-const-perf &lt;span class="ex"&gt;eac6c...&lt;/span&gt;&lt;/span&gt; 257&lt;span id="cb15-14"&gt;&lt;a href="#cb15-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;&amp;gt;&lt;/span&gt; /home/np/worktrees/compiler/improve-const-perf &lt;span class="ex"&gt;eac6c...&lt;/span&gt;&lt;/span&gt;
185&lt;span id="cb15-15"&gt;&lt;a href="#cb15-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;/home/np/worktrees/compiler/improve-std-char-docs&lt;/span&gt; 94cba...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 258&lt;span id="cb15-15"&gt;&lt;a href="#cb15-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;/home/np/worktrees/compiler/improve-std-char-docs&lt;/span&gt; 94cba...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
186&lt;p&gt;Throw some error handling in there, hook up a similar script to improve the UX of &lt;code&gt;git worktree remove&lt;/code&gt;, go wild. A few more helpers I’ve got:&lt;/p&gt; 259&lt;p&gt;Throw some error handling in there, hook up a similar script to
187&lt;div class="sourceCode" id="cb16"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb16-1"&gt;&lt;a href="#cb16-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# gwa /path/to/branch-name&lt;/span&gt;&lt;/span&gt; 260improve the UX of &lt;code&gt;git worktree remove&lt;/code&gt;, go wild. A few more
261helpers I’ve got:&lt;/p&gt;
262&lt;div class="sourceCode" id="cb16"&gt;&lt;pre
263class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb16-1"&gt;&lt;a href="#cb16-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# gwa /path/to/branch-name&lt;/span&gt;&lt;/span&gt;
188&lt;span id="cb16-2"&gt;&lt;a href="#cb16-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# creates a new branch and &amp;quot;switches&amp;quot; to it&lt;/span&gt;&lt;/span&gt; 264&lt;span id="cb16-2"&gt;&lt;a href="#cb16-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# creates a new branch and &amp;quot;switches&amp;quot; to it&lt;/span&gt;&lt;/span&gt;
189&lt;span id="cb16-3"&gt;&lt;a href="#cb16-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;function&lt;/span&gt;&lt;span class="fu"&gt; gwa ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt; 265&lt;span id="cb16-3"&gt;&lt;a href="#cb16-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;function&lt;/span&gt;&lt;span class="fu"&gt; gwa ()&lt;/span&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt;
190&lt;span id="cb16-4"&gt;&lt;a href="#cb16-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="fu"&gt;git&lt;/span&gt; worktree add &lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;span class="va"&gt;$1&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt; &lt;span class="kw"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;span class="va"&gt;$1&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; 266&lt;span id="cb16-4"&gt;&lt;a href="#cb16-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="fu"&gt;git&lt;/span&gt; worktree add &lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;span class="va"&gt;$1&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt; &lt;span class="kw"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;span class="va"&gt;$1&lt;/span&gt;&lt;span class="st"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
@@ -197,88 +273,175 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
197</item> 273</item>
198<item> 274<item>
199<title>Programming On 34 Keys</title> 275<title>Programming On 34 Keys</title>
200<description>&lt;p&gt;Minimizing your keyboard layout is a slippery slope. A few months ago, I built the &lt;a href="https://github.com/icyphox/ferricy"&gt;Ferricy&lt;/a&gt;, a 34-key-split-ortho-ergo keyboard. The Ferricy is a fork of the &lt;a href="https://github.com/davidphilipbarr/Sweep/tree/main/Sweep%20Bling%20MX"&gt;Ferris Sweep MX Bling&lt;/a&gt;.&lt;/p&gt; 276<description>&lt;p&gt;Minimizing your keyboard layout is a slippery slope. A few months
277ago, I built the &lt;a
278href="https://github.com/icyphox/ferricy"&gt;Ferricy&lt;/a&gt;, a
27934-key-split-ortho-ergo keyboard. The Ferricy is a fork of the &lt;a
280href="https://github.com/davidphilipbarr/Sweep/tree/main/Sweep%20Bling%20MX"&gt;Ferris
281Sweep MX Bling&lt;/a&gt;.&lt;/p&gt;
201&lt;figure&gt; 282&lt;figure&gt;
202&lt;img src="https://u.peppe.rs/otz.jpg" alt="The Ferricy, designed by icyphox" /&gt;&lt;figcaption aria-hidden="true"&gt;The Ferricy, designed by &lt;a href="https://icyphox.sh"&gt;icyphox&lt;/a&gt;&lt;/figcaption&gt; 283&lt;img src="https://u.peppe.rs/otz.jpg"
284alt="The Ferricy, designed by icyphox" /&gt;
285&lt;figcaption aria-hidden="true"&gt;The Ferricy, designed by &lt;a
286href="https://icyphox.sh"&gt;icyphox&lt;/a&gt;&lt;/figcaption&gt;
203&lt;/figure&gt; 287&lt;/figure&gt;
204&lt;p&gt;My daily use consists of a bit of prose and a lot of program, my layout has evolved accordingly.&lt;/p&gt; 288&lt;p&gt;My daily use consists of a bit of prose and a lot of program, my
289layout has evolved accordingly.&lt;/p&gt;
205&lt;h1 id="base-layer"&gt;Base Layer&lt;/h1&gt; 290&lt;h1 id="base-layer"&gt;Base Layer&lt;/h1&gt;
206&lt;figure&gt; 291&lt;figure&gt;
207&lt;img src="https://u.peppe.rs/base.png" alt="Colemak with no mods" /&gt;&lt;figcaption aria-hidden="true"&gt;Colemak with no mods&lt;/figcaption&gt; 292&lt;img src="https://u.peppe.rs/base.png" alt="Colemak with no mods" /&gt;
293&lt;figcaption aria-hidden="true"&gt;Colemak with no mods&lt;/figcaption&gt;
208&lt;/figure&gt; 294&lt;/figure&gt;
209&lt;p&gt;The base layer contains alphabets, four symbols and four whitespace keys:&lt;/p&gt; 295&lt;p&gt;The base layer contains alphabets, four symbols and four whitespace
296keys:&lt;/p&gt;
210&lt;ul&gt; 297&lt;ul&gt;
211&lt;li&gt;Alphas: Stock Colemak, with no modifications whatsoever&lt;/li&gt; 298&lt;li&gt;Alphas: Stock Colemak, with no modifications whatsoever&lt;/li&gt;
212&lt;li&gt;Symbols: &lt;code&gt;. , / ;&lt;/code&gt;&lt;/li&gt; 299&lt;li&gt;Symbols: &lt;code&gt;. , / ;&lt;/code&gt;&lt;/li&gt;
213&lt;li&gt;Whitespace: tab, space, enter, backspace (from left to right)&lt;/li&gt; 300&lt;li&gt;Whitespace: tab, space, enter, backspace (from left to right)&lt;/li&gt;
214&lt;/ul&gt; 301&lt;/ul&gt;
215&lt;h1 id="layers"&gt;Layers&lt;/h1&gt; 302&lt;h1 id="layers"&gt;Layers&lt;/h1&gt;
216&lt;p&gt;Keyboard input is complex and it is impossible to skirt around it. You can either use a keyboard with enough keys to supply all possible inputs (a mechanical burden), or you can use firmware to supply all possible inputs (a cognitive burden). Layers are a cognitive burden.&lt;/p&gt; 303&lt;p&gt;Keyboard input is complex and it is impossible to skirt around it.
217&lt;p&gt;I use 3 layers, heavily inspired by &lt;a href="https://github.com/manna-harbour/miryoku"&gt;Miryoku&lt;/a&gt;, but tuned for programming. Excluding the base Colemak layer:&lt;/p&gt; 304You can either use a keyboard with enough keys to supply all possible
305inputs (a mechanical burden), or you can use firmware to supply all
306possible inputs (a cognitive burden). Layers are a cognitive burden.&lt;/p&gt;
307&lt;p&gt;I use 3 layers, heavily inspired by &lt;a
308href="https://github.com/manna-harbour/miryoku"&gt;Miryoku&lt;/a&gt;, but tuned
309for programming. Excluding the base Colemak layer:&lt;/p&gt;
218&lt;ul&gt; 310&lt;ul&gt;
219&lt;li&gt;&lt;code&gt;NAV&lt;/code&gt;: activated on holding &lt;code&gt;space&lt;/code&gt; (left thumb)&lt;/li&gt; 311&lt;li&gt;&lt;code&gt;NAV&lt;/code&gt;: activated on holding &lt;code&gt;space&lt;/code&gt; (left
220&lt;li&gt;&lt;code&gt;NUM&lt;/code&gt;: activated on holding &lt;code&gt;tab&lt;/code&gt; (left thumb)&lt;/li&gt; 312thumb)&lt;/li&gt;
221&lt;li&gt;&lt;code&gt;SYM&lt;/code&gt;: activated on holding &lt;code&gt;enter&lt;/code&gt; (right thumb)&lt;/li&gt; 313&lt;li&gt;&lt;code&gt;NUM&lt;/code&gt;: activated on holding &lt;code&gt;tab&lt;/code&gt; (left
314thumb)&lt;/li&gt;
315&lt;li&gt;&lt;code&gt;SYM&lt;/code&gt;: activated on holding &lt;code&gt;enter&lt;/code&gt; (right
316thumb)&lt;/li&gt;
222&lt;/ul&gt; 317&lt;/ul&gt;
223&lt;h2 id="the-nav-layer"&gt;The &lt;code&gt;NAV&lt;/code&gt; Layer&lt;/h2&gt; 318&lt;h2 id="the-nav-layer"&gt;The &lt;code&gt;NAV&lt;/code&gt; Layer&lt;/h2&gt;
224&lt;p&gt;As the name suggests, this layer is focused on navigation. Arrow keys and the likes.&lt;/p&gt; 319&lt;p&gt;As the name suggests, this layer is focused on navigation. Arrow keys
320and the likes.&lt;/p&gt;
225&lt;figure&gt; 321&lt;figure&gt;
226&lt;img src="https://u.peppe.rs/nav.png" alt="NAV, on holding space" /&gt;&lt;figcaption aria-hidden="true"&gt;&lt;code&gt;NAV&lt;/code&gt;, on holding &lt;code&gt;space&lt;/code&gt;&lt;/figcaption&gt; 322&lt;img src="https://u.peppe.rs/nav.png" alt="NAV, on holding space" /&gt;
323&lt;figcaption aria-hidden="true"&gt;&lt;code&gt;NAV&lt;/code&gt;, on holding
324&lt;code&gt;space&lt;/code&gt;&lt;/figcaption&gt;
227&lt;/figure&gt; 325&lt;/figure&gt;
228&lt;p&gt;Using Vim and Colemak means you lose out on HJKL navigation. However, on activating the &lt;code&gt;NAV&lt;/code&gt; layer, the right home-row is converted into arrow keys. In essence, by holding space, I can navigate Vim with the home-row, or Firefox, or my PDF reader. I no longer need to look for software that allows Vim navigation keys, because it is baked into the firmware!&lt;/p&gt; 326&lt;p&gt;Using Vim and Colemak means you lose out on HJKL navigation. However,
229&lt;p&gt;My Vim motions are not limited to HJKL. In fact, my Vim motions are rarely HJKL. I tend to use &lt;code&gt;}&lt;/code&gt; (next paragraph) and &lt;code&gt;)&lt;/code&gt; (next sentence) more often. As a result, these have found their way into my &lt;code&gt;NAV&lt;/code&gt; layer, over the likes of &lt;code&gt;PgDown&lt;/code&gt; and &lt;code&gt;End&lt;/code&gt;. Having brackets at my index and middle fingers is nice for programming too.&lt;/p&gt; 327on activating the &lt;code&gt;NAV&lt;/code&gt; layer, the right home-row is
328converted into arrow keys. In essence, by holding space, I can navigate
329Vim with the home-row, or Firefox, or my PDF reader. I no longer need to
330look for software that allows Vim navigation keys, because it is baked
331into the firmware!&lt;/p&gt;
332&lt;p&gt;My Vim motions are not limited to HJKL. In fact, my Vim motions are
333rarely HJKL. I tend to use &lt;code&gt;}&lt;/code&gt; (next paragraph) and
334&lt;code&gt;)&lt;/code&gt; (next sentence) more often. As a result, these have found
335their way into my &lt;code&gt;NAV&lt;/code&gt; layer, over the likes of
336&lt;code&gt;PgDown&lt;/code&gt; and &lt;code&gt;End&lt;/code&gt;. Having brackets at my index
337and middle fingers is nice for programming too.&lt;/p&gt;
230&lt;h2 id="the-sym-layer"&gt;The &lt;code&gt;SYM&lt;/code&gt; Layer&lt;/h2&gt; 338&lt;h2 id="the-sym-layer"&gt;The &lt;code&gt;SYM&lt;/code&gt; Layer&lt;/h2&gt;
231&lt;figure&gt; 339&lt;figure&gt;
232&lt;img src="https://u.peppe.rs/sym.png" alt="SYM, on holding enter" /&gt;&lt;figcaption aria-hidden="true"&gt;&lt;code&gt;SYM&lt;/code&gt;, on holding &lt;code&gt;enter&lt;/code&gt;&lt;/figcaption&gt; 340&lt;img src="https://u.peppe.rs/sym.png" alt="SYM, on holding enter" /&gt;
341&lt;figcaption aria-hidden="true"&gt;&lt;code&gt;SYM&lt;/code&gt;, on holding
342&lt;code&gt;enter&lt;/code&gt;&lt;/figcaption&gt;
233&lt;/figure&gt; 343&lt;/figure&gt;
234&lt;p&gt;This layer contains all the symbols that you would find by hitting &lt;code&gt;Shift&lt;/code&gt; and a key on the number row. Probably noteworthy to Vim users: the symbols are arranged in the form of a mirrored numpad for exactly one reason: to move &lt;code&gt;$&lt;/code&gt; to the left of &lt;code&gt;^&lt;/code&gt;. It has always annoyed me that &lt;code&gt;$&lt;/code&gt; moves the cursor to the end of the line and &lt;code&gt;^&lt;/code&gt; moves it to the beginning, but their position on a typical number row are reversed, 4 comes before 6.&lt;/p&gt; 344&lt;p&gt;This layer contains all the symbols that you would find by hitting
345&lt;code&gt;Shift&lt;/code&gt; and a key on the number row. Probably noteworthy to
346Vim users: the symbols are arranged in the form of a mirrored numpad for
347exactly one reason: to move &lt;code&gt;$&lt;/code&gt; to the left of
348&lt;code&gt;^&lt;/code&gt;. It has always annoyed me that &lt;code&gt;$&lt;/code&gt; moves the
349cursor to the end of the line and &lt;code&gt;^&lt;/code&gt; moves it to the
350beginning, but their position on a typical number row are reversed, 4
351comes before 6.&lt;/p&gt;
235&lt;h2 id="the-num-layer"&gt;The &lt;code&gt;NUM&lt;/code&gt; layer&lt;/h2&gt; 352&lt;h2 id="the-num-layer"&gt;The &lt;code&gt;NUM&lt;/code&gt; layer&lt;/h2&gt;
236&lt;figure&gt; 353&lt;figure&gt;
237&lt;img src="https://u.peppe.rs/num.png" alt="NUM, on holding tab" /&gt;&lt;figcaption aria-hidden="true"&gt;&lt;code&gt;NUM&lt;/code&gt;, on holding &lt;code&gt;tab&lt;/code&gt;&lt;/figcaption&gt; 354&lt;img src="https://u.peppe.rs/num.png" alt="NUM, on holding tab" /&gt;
355&lt;figcaption aria-hidden="true"&gt;&lt;code&gt;NUM&lt;/code&gt;, on holding
356&lt;code&gt;tab&lt;/code&gt;&lt;/figcaption&gt;
238&lt;/figure&gt; 357&lt;/figure&gt;
239&lt;p&gt;Another deviation from Miryoku, the numpad just feels &lt;em&gt;right&lt;/em&gt; on my &lt;em&gt;right&lt;/em&gt; hand.&lt;/p&gt; 358&lt;p&gt;Another deviation from Miryoku, the numpad just feels &lt;em&gt;right&lt;/em&gt;
359on my &lt;em&gt;right&lt;/em&gt; hand.&lt;/p&gt;
240&lt;h1 id="zmk-combos"&gt;ZMK Combos&lt;/h1&gt; 360&lt;h1 id="zmk-combos"&gt;ZMK Combos&lt;/h1&gt;
241&lt;p&gt;If you have been paying close attention, you might have noticed that &lt;code&gt;escape&lt;/code&gt; didn’t make it to any layer. &lt;code&gt;escape&lt;/code&gt; is too crucial to put on a non-base layer, but at the same time, not as important to deserve a place on the base layer. That is where ZMK’s combos come in. Combos let you tap any number of keys, and combine them to form a single key. I have combos set up for underscore, minus, escape and caps-word (more on caps-word later):&lt;/p&gt; 361&lt;p&gt;If you have been paying close attention, you might have noticed that
362&lt;code&gt;escape&lt;/code&gt; didn’t make it to any layer. &lt;code&gt;escape&lt;/code&gt; is
363too crucial to put on a non-base layer, but at the same time, not as
364important to deserve a place on the base layer. That is where ZMK’s
365combos come in. Combos let you tap any number of keys, and combine them
366to form a single key. I have combos set up for underscore, minus, escape
367and caps-word (more on caps-word later):&lt;/p&gt;
242&lt;figure&gt; 368&lt;figure&gt;
243&lt;img src="https://u.peppe.rs/combos.png" alt="Combos are almost piano-like" /&gt;&lt;figcaption aria-hidden="true"&gt;Combos are almost piano-like&lt;/figcaption&gt; 369&lt;img src="https://u.peppe.rs/combos.png"
370alt="Combos are almost piano-like" /&gt;
371&lt;figcaption aria-hidden="true"&gt;Combos are almost piano-like&lt;/figcaption&gt;
244&lt;/figure&gt; 372&lt;/figure&gt;
245&lt;h1 id="home-row-mods"&gt;Home-row Mods&lt;/h1&gt; 373&lt;h1 id="home-row-mods"&gt;Home-row Mods&lt;/h1&gt;
246&lt;p&gt;Inherited from Miryoku, I have home-row mods for activating &lt;code&gt;Super&lt;/code&gt;, &lt;code&gt;Alt&lt;/code&gt;, &lt;code&gt;Shift&lt;/code&gt;, &lt;code&gt;Ctrl&lt;/code&gt; and &lt;code&gt;Hyper&lt;/code&gt; (&lt;code&gt;Ctrl + Shift + Alt + Super&lt;/code&gt;). The idea is to send &lt;code&gt;T&lt;/code&gt; on tap and &lt;code&gt;Ctrl&lt;/code&gt; on hold. Home-row mods are fairly popular, so I’ll not go into the details.&lt;/p&gt; 374&lt;p&gt;Inherited from Miryoku, I have home-row mods for activating
375&lt;code&gt;Super&lt;/code&gt;, &lt;code&gt;Alt&lt;/code&gt;, &lt;code&gt;Shift&lt;/code&gt;,
376&lt;code&gt;Ctrl&lt;/code&gt; and &lt;code&gt;Hyper&lt;/code&gt;
377(&lt;code&gt;Ctrl + Shift + Alt + Super&lt;/code&gt;). The idea is to send
378&lt;code&gt;T&lt;/code&gt; on tap and &lt;code&gt;Ctrl&lt;/code&gt; on hold. Home-row mods are
379fairly popular, so I’ll not go into the details.&lt;/p&gt;
247&lt;figure&gt; 380&lt;figure&gt;
248&lt;img src="https://u.peppe.rs/homerow.png" alt="Super, Alt, Shift, Ctrl, Hyper; on the left half, and mirrored on the right half" /&gt;&lt;figcaption aria-hidden="true"&gt;Super, Alt, Shift, Ctrl, Hyper; on the left half, and mirrored on the right half&lt;/figcaption&gt; 381&lt;img src="https://u.peppe.rs/homerow.png"
382alt="Super, Alt, Shift, Ctrl, Hyper; on the left half, and mirrored on the right half" /&gt;
383&lt;figcaption aria-hidden="true"&gt;Super, Alt, Shift, Ctrl, Hyper; on the
384left half, and mirrored on the right half&lt;/figcaption&gt;
249&lt;/figure&gt; 385&lt;/figure&gt;
250&lt;p&gt;&lt;code&gt;Hyper&lt;/code&gt; bridges the gap between firmware and software. You can never configure key combination that, opens Firefox, for example, through firmware alone. However, with the &lt;code&gt;Hyper&lt;/code&gt; key, and some &lt;code&gt;sxhkd&lt;/code&gt; magic, you can emulate that. Pressing &lt;code&gt;Hyper + F&lt;/code&gt; on a keyboard is just two keys, but the key codes sent are &lt;code&gt;Ctrl + Shift + Alt + Super + F&lt;/code&gt;. That key combination is not intercepted by any application as a shortcut, except for the following &lt;code&gt;sxhkd&lt;/code&gt; stanza:&lt;/p&gt; 386&lt;p&gt;&lt;code&gt;Hyper&lt;/code&gt; bridges the gap between firmware and software. You
251&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;super&lt;/span&gt; + alt + shift + ctrl + f&lt;/span&gt; 387can never configure key combination that, opens Firefox, for example,
388through firmware alone. However, with the &lt;code&gt;Hyper&lt;/code&gt; key, and
389some &lt;code&gt;sxhkd&lt;/code&gt; magic, you can emulate that. Pressing
390&lt;code&gt;Hyper + F&lt;/code&gt; on a keyboard is just two keys, but the key codes
391sent are &lt;code&gt;Ctrl + Shift + Alt + Super + F&lt;/code&gt;. That key
392combination is not intercepted by any application as a shortcut, except
393for the following &lt;code&gt;sxhkd&lt;/code&gt; stanza:&lt;/p&gt;
394&lt;div class="sourceCode" id="cb1"&gt;&lt;pre
395class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;super&lt;/span&gt; + alt + shift + ctrl + f&lt;/span&gt;
252&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;xdotool&lt;/span&gt; search &lt;span class="st"&gt;&amp;quot;Mozilla Firefox&amp;quot;&lt;/span&gt; windowactivate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 396&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;xdotool&lt;/span&gt; search &lt;span class="st"&gt;&amp;quot;Mozilla Firefox&amp;quot;&lt;/span&gt; windowactivate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
253&lt;p&gt;Alternatively, you can intercept unused &lt;code&gt;F&lt;/code&gt; keys: &lt;code&gt;F13&lt;/code&gt; through &lt;code&gt;F24&lt;/code&gt;.&lt;/p&gt; 397&lt;p&gt;Alternatively, you can intercept unused &lt;code&gt;F&lt;/code&gt; keys:
254&lt;p&gt;Home-row mods are mirrored on each half because it would be impossible to hit &lt;code&gt;Ctrl + T&lt;/code&gt; if not; they lie on the same key.&lt;/p&gt; 398&lt;code&gt;F13&lt;/code&gt; through &lt;code&gt;F24&lt;/code&gt;.&lt;/p&gt;
399&lt;p&gt;Home-row mods are mirrored on each half because it would be
400impossible to hit &lt;code&gt;Ctrl + T&lt;/code&gt; if not; they lie on the same
401key.&lt;/p&gt;
255&lt;h1 id="caps-word"&gt;Caps-word&lt;/h1&gt; 402&lt;h1 id="caps-word"&gt;Caps-word&lt;/h1&gt;
256&lt;p&gt;Caps-word is a clever caps-lock, built into ZMK. Typing out constants such as &lt;code&gt;PORT&lt;/code&gt; with home-row mods would look like this:&lt;/p&gt; 403&lt;p&gt;Caps-word is a clever caps-lock, built into ZMK. Typing out constants
404such as &lt;code&gt;PORT&lt;/code&gt; with home-row mods would look like this:&lt;/p&gt;
257&lt;ul&gt; 405&lt;ul&gt;
258&lt;li&gt;hold &lt;code&gt;e&lt;/code&gt; (shift) on left hand, and tap &lt;code&gt;p&lt;/code&gt; on right hand&lt;/li&gt; 406&lt;li&gt;hold &lt;code&gt;e&lt;/code&gt; (shift) on left hand, and tap &lt;code&gt;p&lt;/code&gt; on
259&lt;li&gt;hold &lt;code&gt;e&lt;/code&gt; (shift) on left hand, and tap &lt;code&gt;o&lt;/code&gt; on right hand&lt;/li&gt; 407right hand&lt;/li&gt;
260&lt;li&gt;hold &lt;code&gt;s&lt;/code&gt; (shift) on right hand, and tap &lt;code&gt;r&lt;/code&gt; on left hand&lt;/li&gt; 408&lt;li&gt;hold &lt;code&gt;e&lt;/code&gt; (shift) on left hand, and tap &lt;code&gt;o&lt;/code&gt; on
261&lt;li&gt;hold &lt;code&gt;s&lt;/code&gt; (shift) on right hand, and tap &lt;code&gt;t&lt;/code&gt; on left hand&lt;/li&gt; 409right hand&lt;/li&gt;
410&lt;li&gt;hold &lt;code&gt;s&lt;/code&gt; (shift) on right hand, and tap &lt;code&gt;r&lt;/code&gt; on
411left hand&lt;/li&gt;
412&lt;li&gt;hold &lt;code&gt;s&lt;/code&gt; (shift) on right hand, and tap &lt;code&gt;t&lt;/code&gt; on
413left hand&lt;/li&gt;
262&lt;/ul&gt; 414&lt;/ul&gt;
263&lt;p&gt;This hold-alternate-hold dance gets tiring quickly. With caps-word, however:&lt;/p&gt; 415&lt;p&gt;This hold-alternate-hold dance gets tiring quickly. With caps-word,
416however:&lt;/p&gt;
264&lt;ul&gt; 417&lt;ul&gt;
265&lt;li&gt;toggle &lt;code&gt;caps_word&lt;/code&gt;&lt;/li&gt; 418&lt;li&gt;toggle &lt;code&gt;caps_word&lt;/code&gt;&lt;/li&gt;
266&lt;li&gt;type out &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;o&lt;/code&gt;, &lt;code&gt;r&lt;/code&gt;, &lt;code&gt;t&lt;/code&gt;&lt;/li&gt; 419&lt;li&gt;type out &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;o&lt;/code&gt;, &lt;code&gt;r&lt;/code&gt;,
420&lt;code&gt;t&lt;/code&gt;&lt;/li&gt;
267&lt;li&gt;hit a &lt;em&gt;break&lt;/em&gt; character (space, enter will do)&lt;/li&gt; 421&lt;li&gt;hit a &lt;em&gt;break&lt;/em&gt; character (space, enter will do)&lt;/li&gt;
268&lt;li&gt;continue&lt;/li&gt; 422&lt;li&gt;continue&lt;/li&gt;
269&lt;/ul&gt; 423&lt;/ul&gt;
270&lt;p&gt;Caps-word automatically disables capitalization upon encountering a breaking character, (which are space, enter or any modifier, by default) right in the firmware!&lt;/p&gt; 424&lt;p&gt;Caps-word automatically disables capitalization upon encountering a
425breaking character, (which are space, enter or any modifier, by default)
426right in the firmware!&lt;/p&gt;
271&lt;h1 id="findings"&gt;Findings&lt;/h1&gt; 427&lt;h1 id="findings"&gt;Findings&lt;/h1&gt;
272&lt;p&gt;34-keys has been reasonably comfortable to use, for both prose and program. My palms do not move across the desk at all, as I reach for keys. I mostly write Rust and Bash, and my layout has evolved to accomodate special characters from their grammars (angled brackets and hyphens, specifically). If you are on a similar journey, I would suggest focusing on accuracy and comfort over speed. Speed comes with time.&lt;/p&gt;</description> 428&lt;p&gt;34-keys has been reasonably comfortable to use, for both prose and
429program. My palms do not move across the desk at all, as I reach for
430keys. I mostly write Rust and Bash, and my layout has evolved to
431accomodate special characters from their grammars (angled brackets and
432hyphens, specifically). If you are on a similar journey, I would suggest
433focusing on accuracy and comfort over speed. Speed comes with time.&lt;/p&gt;</description>
273<link>https://peppe.rs/posts/programming_on_34_keys/</link> 434<link>https://peppe.rs/posts/programming_on_34_keys/</link>
274<pubDate>Sun, 28 Aug 2022 13:51:00 +0000</pubDate> 435<pubDate>Sun, 28 Aug 2022 13:51:00 +0000</pubDate>
275<guid>https://peppe.rs/posts/programming_on_34_keys/</guid> 436<guid>https://peppe.rs/posts/programming_on_34_keys/</guid>
276</item> 437</item>
277<item> 438<item>
278<title>A Reference Counted Afterlife</title> 439<title>A Reference Counted Afterlife</title>
279<description>&lt;p&gt;I took interest in the Egyptian rendition of the afterlife recently.&lt;/p&gt; 440<description>&lt;p&gt;I took interest in the Egyptian rendition of the afterlife
441recently.&lt;/p&gt;
280&lt;h3 id="parts-of-the-soul"&gt;Parts of the Soul&lt;/h3&gt; 442&lt;h3 id="parts-of-the-soul"&gt;Parts of the Soul&lt;/h3&gt;
281&lt;p&gt;Ancient Egyptians believed that the soul comprised of several components:&lt;/p&gt; 443&lt;p&gt;Ancient Egyptians believed that the soul comprised of several
444components:&lt;/p&gt;
282&lt;ul&gt; 445&lt;ul&gt;
283&lt;li&gt;&lt;em&gt;ren&lt;/em&gt;&lt;/li&gt; 446&lt;li&gt;&lt;em&gt;ren&lt;/em&gt;&lt;/li&gt;
284&lt;li&gt;&lt;em&gt;ka&lt;/em&gt;&lt;/li&gt; 447&lt;li&gt;&lt;em&gt;ka&lt;/em&gt;&lt;/li&gt;
@@ -286,47 +449,80 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
286&lt;li&gt;&lt;em&gt;ba&lt;/em&gt;&lt;/li&gt; 449&lt;li&gt;&lt;em&gt;ba&lt;/em&gt;&lt;/li&gt;
287&lt;li&gt;&lt;em&gt;sheut&lt;/em&gt;&lt;/li&gt; 450&lt;li&gt;&lt;em&gt;sheut&lt;/em&gt;&lt;/li&gt;
288&lt;/ul&gt; 451&lt;/ul&gt;
289&lt;p&gt;Egyptians emphasized on preserving the different parts of the soul. Mummification for example, served to preserve the physical part of the soul. The other components have their respective preservation strategies.&lt;/p&gt; 452&lt;p&gt;Egyptians emphasized on preserving the different parts of the soul.
290&lt;p&gt;Of all of these bits, I find &lt;em&gt;ren&lt;/em&gt;, which simply means &lt;em&gt;name&lt;/em&gt;, to be the most interesting. &lt;em&gt;Ba&lt;/em&gt;, the human-headed chicken that represents &lt;em&gt;personality&lt;/em&gt;, is a close favourite.&lt;/p&gt; 453Mummification for example, served to preserve the physical part of the
291&lt;p&gt;&lt;em&gt;Ren&lt;/em&gt; is the name given to a person at birth. Egyptians believed that this portion of the soul would continue to live on for as long as it was spoken. If you were someone worthy of continued existence, your name would be inscribed all over the place. If you were the type to snatch away bread from children, your name would be condemned from memory, forgotten.&lt;/p&gt; 454soul. The other components have their respective preservation
455strategies.&lt;/p&gt;
456&lt;p&gt;Of all of these bits, I find &lt;em&gt;ren&lt;/em&gt;, which simply means
457&lt;em&gt;name&lt;/em&gt;, to be the most interesting. &lt;em&gt;Ba&lt;/em&gt;, the human-headed
458chicken that represents &lt;em&gt;personality&lt;/em&gt;, is a close favourite.&lt;/p&gt;
459&lt;p&gt;&lt;em&gt;Ren&lt;/em&gt; is the name given to a person at birth. Egyptians
460believed that this portion of the soul would continue to live on for as
461long as it was spoken. If you were someone worthy of continued
462existence, your name would be inscribed all over the place. If you were
463the type to snatch away bread from children, your name would be
464condemned from memory, forgotten.&lt;/p&gt;
292&lt;h3 id="garbage-collection"&gt;Garbage-collection&lt;/h3&gt; 465&lt;h3 id="garbage-collection"&gt;Garbage-collection&lt;/h3&gt;
293&lt;p&gt;The concept of &lt;em&gt;ren&lt;/em&gt; seems to be perfectly analogous to reference counted garbage-collection.&lt;/p&gt; 466&lt;p&gt;The concept of &lt;em&gt;ren&lt;/em&gt; seems to be perfectly analogous to
467reference counted garbage-collection.&lt;/p&gt;
294&lt;ul&gt; 468&lt;ul&gt;
295&lt;li&gt;A name (&lt;em&gt;ren&lt;/em&gt;) is assigned to an object (person) on initialization (at birth)&lt;/li&gt; 469&lt;li&gt;A name (&lt;em&gt;ren&lt;/em&gt;) is assigned to an object (person) on
470initialization (at birth)&lt;/li&gt;
296&lt;li&gt;Names are used to refer to objects&lt;/li&gt; 471&lt;li&gt;Names are used to refer to objects&lt;/li&gt;
297&lt;li&gt;Objects go out of existence when there are no more references to them&lt;/li&gt; 472&lt;li&gt;Objects go out of existence when there are no more references to
473them&lt;/li&gt;
298&lt;/ul&gt; 474&lt;/ul&gt;
299&lt;p&gt;The concept of &lt;em&gt;ren&lt;/em&gt; seems to model human-memory. The similarity with garbage-collection is now easily explained, because garbage-collection models a program’s memory.&lt;/p&gt; 475&lt;p&gt;The concept of &lt;em&gt;ren&lt;/em&gt; seems to model human-memory. The
300&lt;p&gt;Perhaps some cheeky Egyptian has attained immortality by creating a &lt;em&gt;ren&lt;/em&gt;-cycle.&lt;/p&gt;</description> 476similarity with garbage-collection is now easily explained, because
477garbage-collection models a program’s memory.&lt;/p&gt;
478&lt;p&gt;Perhaps some cheeky Egyptian has attained immortality by creating a
479&lt;em&gt;ren&lt;/em&gt;-cycle.&lt;/p&gt;</description>
301<link>https://peppe.rs/posts/a_reference_counted_afterlife/</link> 480<link>https://peppe.rs/posts/a_reference_counted_afterlife/</link>
302<pubDate>Tue, 02 Aug 2022 16:47:00 +0000</pubDate> 481<pubDate>Tue, 02 Aug 2022 16:47:00 +0000</pubDate>
303<guid>https://peppe.rs/posts/a_reference_counted_afterlife/</guid> 482<guid>https://peppe.rs/posts/a_reference_counted_afterlife/</guid>
304</item> 483</item>
305<item> 484<item>
306<title>Lotus58</title> 485<title>Lotus58</title>
307<description>&lt;p&gt;Earlier this month, I decided that I would laugh at Indian customs in the face by building a split-ergo mechanical keyboard from scratch rather than purchasing a Moonlander.&lt;/p&gt; 486<description>&lt;p&gt;Earlier this month, I decided that I would laugh at Indian customs in
487the face by building a split-ergo mechanical keyboard from scratch
488rather than purchasing a Moonlander.&lt;/p&gt;
308&lt;figure&gt; 489&lt;figure&gt;
309&lt;img src="https://u.peppe.rs/i8k.jpg" alt="The finished product" /&gt;&lt;figcaption aria-hidden="true"&gt;The finished product&lt;/figcaption&gt; 490&lt;img src="https://u.peppe.rs/i8k.jpg" alt="The finished product" /&gt;
491&lt;figcaption aria-hidden="true"&gt;The finished product&lt;/figcaption&gt;
310&lt;/figure&gt; 492&lt;/figure&gt;
311&lt;h2 id="sourcing-the-parts"&gt;Sourcing the parts&lt;/h2&gt; 493&lt;h2 id="sourcing-the-parts"&gt;Sourcing the parts&lt;/h2&gt;
312&lt;p&gt;If you, like me, live in India, you might find this section useful. My approach to finding parts:&lt;/p&gt; 494&lt;p&gt;If you, like me, live in India, you might find this section useful.
495My approach to finding parts:&lt;/p&gt;
313&lt;ul&gt; 496&lt;ul&gt;
314&lt;li&gt;Check reputed, local online stores&lt;/li&gt; 497&lt;li&gt;Check reputed, local online stores&lt;/li&gt;
315&lt;li&gt;Check physical hardware stores&lt;/li&gt; 498&lt;li&gt;Check physical hardware stores&lt;/li&gt;
316&lt;li&gt;Import the part&lt;/li&gt; 499&lt;li&gt;Import the part&lt;/li&gt;
317&lt;/ul&gt; 500&lt;/ul&gt;
318&lt;h3 id="pcbs"&gt;PCBs&lt;/h3&gt; 501&lt;h3 id="pcbs"&gt;PCBs&lt;/h3&gt;
319&lt;p&gt;This was by far the hardest component to procure. Fabrication services have certain &lt;em&gt;capabilities&lt;/em&gt;. Capabilities are the limitations of a fabrication service. For example, a service may be capable of drilling holes no smaller than 0.3mm in diameter. Most sites have a verification process to check if their capabilities meet your design’s requirements. I tried a few local PCB fabrication services:&lt;/p&gt; 502&lt;p&gt;This was by far the hardest component to procure. Fabrication
503services have certain &lt;em&gt;capabilities&lt;/em&gt;. Capabilities are the
504limitations of a fabrication service. For example, a service may be
505capable of drilling holes no smaller than 0.3mm in diameter. Most sites
506have a verification process to check if their capabilities meet your
507design’s requirements. I tried a few local PCB fabrication services:&lt;/p&gt;
320&lt;ul&gt; 508&lt;ul&gt;
321&lt;li&gt;Lion PCB: Capabilities did not meet my requirements&lt;/li&gt; 509&lt;li&gt;Lion PCB: Capabilities did not meet my requirements&lt;/li&gt;
322&lt;li&gt;PCBPower: Capabilities did not meet my requirements&lt;/li&gt; 510&lt;li&gt;PCBPower: Capabilities did not meet my requirements&lt;/li&gt;
323&lt;li&gt;Circuitwala: Capabilities did not meet my requirements&lt;/li&gt; 511&lt;li&gt;Circuitwala: Capabilities did not meet my requirements&lt;/li&gt;
324&lt;/ul&gt; 512&lt;/ul&gt;
325&lt;p&gt;I settled for JLCPCB, a Chinese service. PCBs themselves were 16 USD, shipping was another 35 USD, and customs was another 3.5K INR (ouch).&lt;/p&gt; 513&lt;p&gt;I settled for JLCPCB, a Chinese service. PCBs themselves were 16 USD,
514shipping was another 35 USD, and customs was another 3.5K INR
515(ouch).&lt;/p&gt;
326&lt;h3 id="case-material"&gt;Case material&lt;/h3&gt; 516&lt;h3 id="case-material"&gt;Case material&lt;/h3&gt;
327&lt;p&gt;I don’t really have a case for the Lotus58, it is more of a “plastic sandwich”. I purchased acrylic plates from Robu. Cheap, fast, solid, and customizable. I cannot recommend Robu enough. A full set of plates (2 top plates and 2 bottom plates) cost me about 500 INR. I also bought a pair of laptop height raisers on Amazon to create a budget tenting setup.&lt;/p&gt; 517&lt;p&gt;I don’t really have a case for the Lotus58, it is more of a “plastic
518sandwich”. I purchased acrylic plates from Robu. Cheap, fast, solid, and
519customizable. I cannot recommend Robu enough. A full set of plates (2
520top plates and 2 bottom plates) cost me about 500 INR. I also bought a
521pair of laptop height raisers on Amazon to create a budget tenting
522setup.&lt;/p&gt;
328&lt;h3 id="electronics"&gt;Electronics&lt;/h3&gt; 523&lt;h3 id="electronics"&gt;Electronics&lt;/h3&gt;
329&lt;p&gt;You’ll need a few rather specific electronic components such as hotswap sockets and TRRS mounts, the rest are commonly available:&lt;/p&gt; 524&lt;p&gt;You’ll need a few rather specific electronic components such as
525hotswap sockets and TRRS mounts, the rest are commonly available:&lt;/p&gt;
330&lt;ul&gt; 526&lt;ul&gt;
331&lt;li&gt;Hotswap sockets: StacksKB&lt;/li&gt; 527&lt;li&gt;Hotswap sockets: StacksKB&lt;/li&gt;
332&lt;li&gt;TRRS mounts (PJ 320A): StacksKB&lt;/li&gt; 528&lt;li&gt;TRRS mounts (PJ 320A): StacksKB&lt;/li&gt;
@@ -335,7 +531,8 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
335&lt;li&gt;M2 spacers: ThinkRobotics&lt;/li&gt; 531&lt;li&gt;M2 spacers: ThinkRobotics&lt;/li&gt;
336&lt;li&gt;Arduino Pro Micro: Robu&lt;/li&gt; 532&lt;li&gt;Arduino Pro Micro: Robu&lt;/li&gt;
337&lt;/ul&gt; 533&lt;/ul&gt;
338&lt;p&gt;I skimped out on optional components such as OLEDs and rotary encoders.&lt;/p&gt; 534&lt;p&gt;I skimped out on optional components such as OLEDs and rotary
535encoders.&lt;/p&gt;
339&lt;h3 id="switches-and-keycaps"&gt;Switches and Keycaps&lt;/h3&gt; 536&lt;h3 id="switches-and-keycaps"&gt;Switches and Keycaps&lt;/h3&gt;
340&lt;p&gt;Arguably the most fun part of the build:&lt;/p&gt; 537&lt;p&gt;Arguably the most fun part of the build:&lt;/p&gt;
341&lt;ul&gt; 538&lt;ul&gt;
@@ -343,30 +540,57 @@ aa857eb953e Auto merge of #100537 - petrochenkov:pic ...
343&lt;li&gt;DSA blanks: Meckeys&lt;/li&gt; 540&lt;li&gt;DSA blanks: Meckeys&lt;/li&gt;
344&lt;/ul&gt; 541&lt;/ul&gt;
345&lt;h2 id="building-the-keyboard"&gt;Building the keyboard&lt;/h2&gt; 542&lt;h2 id="building-the-keyboard"&gt;Building the keyboard&lt;/h2&gt;
346&lt;p&gt;The the build is extremely straightforward. Through hole components are easy to solder. Be wary of component placement and orientation. Check thrice, solder once. Few debugging tips:&lt;/p&gt; 543&lt;p&gt;The the build is extremely straightforward. Through hole components
544are easy to solder. Be wary of component placement and orientation.
545Check thrice, solder once. Few debugging tips:&lt;/p&gt;
347&lt;ul&gt; 546&lt;ul&gt;
348&lt;li&gt;if a single key does not actuate, check the hotswap for poor soldering (reflow the joint), and the switch pins for deformation during installation&lt;/li&gt; 547&lt;li&gt;if a single key does not actuate, check the hotswap for poor
349&lt;li&gt;if an entire column or row activates on a single key-press, a connection has been shorted&lt;/li&gt; 548soldering (reflow the joint), and the switch pins for deformation during
350&lt;li&gt;if only some of the keys on a given row actuate, a diode has been soldered on the wrong way&lt;/li&gt; 549installation&lt;/li&gt;
550&lt;li&gt;if an entire column or row activates on a single key-press, a
551connection has been shorted&lt;/li&gt;
552&lt;li&gt;if only some of the keys on a given row actuate, a diode has been
553soldered on the wrong way&lt;/li&gt;
351&lt;/ul&gt; 554&lt;/ul&gt;
352&lt;h2 id="the-typing-experience"&gt;The typing experience&lt;/h2&gt; 555&lt;h2 id="the-typing-experience"&gt;The typing experience&lt;/h2&gt;
353&lt;p&gt;I decidede to give QWERTY the boot and learn Colemak along with the new keyboard. The first few weeks were terrible because I could neither type QWERTY nor Colemak, but I got the hang of it pretty quickly. Typing websites do help, but it is best to simply use it in your daily workflow. No site can help you get accustomed to the various things you use your keyboard for such as switching windows or navigating vim/tmux.&lt;/p&gt; 556&lt;p&gt;I decidede to give QWERTY the boot and learn Colemak along with the
557new keyboard. The first few weeks were terrible because I could neither
558type QWERTY nor Colemak, but I got the hang of it pretty quickly. Typing
559websites do help, but it is best to simply use it in your daily
560workflow. No site can help you get accustomed to the various things you
561use your keyboard for such as switching windows or navigating
562vim/tmux.&lt;/p&gt;
354&lt;h3 id="colemak"&gt;Colemak&lt;/h3&gt; 563&lt;h3 id="colemak"&gt;Colemak&lt;/h3&gt;
355&lt;p&gt;Alt layouts such as Colemak are definitely worth it. I find that Colemak reduces finger movement a lot, a good portion of the keys on the left hand are the same as QWERTY, it is fairly easy to pick up as well.&lt;/p&gt; 564&lt;p&gt;Alt layouts such as Colemak are definitely worth it. I find that
565Colemak reduces finger movement a lot, a good portion of the keys on the
566left hand are the same as QWERTY, it is fairly easy to pick up as
567well.&lt;/p&gt;
356&lt;h3 id="vim"&gt;Vim&lt;/h3&gt; 568&lt;h3 id="vim"&gt;Vim&lt;/h3&gt;
357&lt;p&gt;Using an alt layout means most programs with keyboard shortcuts are not going to work as expected, &lt;code&gt;HJKL&lt;/code&gt; on vim for movements being one of them. I took the short route out by creating a new layer with arrow keys on the home row:&lt;/p&gt; 569&lt;p&gt;Using an alt layout means most programs with keyboard shortcuts are
570not going to work as expected, &lt;code&gt;HJKL&lt;/code&gt; on vim for movements
571being one of them. I took the short route out by creating a new layer
572with arrow keys on the home row:&lt;/p&gt;
358&lt;pre&gt;&lt;code&gt;default homerow: 573&lt;pre&gt;&lt;code&gt;default homerow:
359H N E I 574H N E I
360 575
361&amp;quot;nav&amp;quot; layer: 576&amp;quot;nav&amp;quot; layer:
362&amp;lt; v ^ &amp;gt;&lt;/code&gt;&lt;/pre&gt; 577&amp;lt; v ^ &amp;gt;&lt;/code&gt;&lt;/pre&gt;
363&lt;p&gt;The remaining commands in vim are largely mnemonics. Navigating with home-row arrow keys also means that I can use “HJKL” globally, to scroll a website, for example.&lt;/p&gt; 578&lt;p&gt;The remaining commands in vim are largely mnemonics. Navigating with
579home-row arrow keys also means that I can use “HJKL” globally, to scroll
580a website, for example.&lt;/p&gt;
364&lt;h3 id="cutting-down-to-34-keys"&gt;Cutting down to 34 keys&lt;/h3&gt; 581&lt;h3 id="cutting-down-to-34-keys"&gt;Cutting down to 34 keys&lt;/h3&gt;
365&lt;p&gt;A couple months into my ergo journey, I realized that I could get away by moving my fingers even lesser. I moved modifiers such as &lt;code&gt;Super&lt;/code&gt;, &lt;code&gt;Ctrl&lt;/code&gt;, &lt;code&gt;Alt&lt;/code&gt; and &lt;code&gt;Shift&lt;/code&gt; to the home-row as QMK Mod Taps. The rest of the keys are cleverly placed in layers, not too much unlike the Miryoku layout. Even for someone that writes Rust (a symbol-heavy grammar), I find 34-keys to be sufficient.&lt;/p&gt; 582&lt;p&gt;A couple months into my ergo journey, I realized that I could get
583away by moving my fingers even lesser. I moved modifiers such as
584&lt;code&gt;Super&lt;/code&gt;, &lt;code&gt;Ctrl&lt;/code&gt;, &lt;code&gt;Alt&lt;/code&gt; and
585&lt;code&gt;Shift&lt;/code&gt; to the home-row as QMK Mod Taps. The rest of the keys
586are cleverly placed in layers, not too much unlike the Miryoku layout.
587Even for someone that writes Rust (a symbol-heavy grammar), I find
58834-keys to be sufficient.&lt;/p&gt;
366&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 589&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
367&lt;p&gt;I have been bitten by the ergomech bug.&lt;/p&gt; 590&lt;p&gt;I have been bitten by the ergomech bug.&lt;/p&gt;
368&lt;figure&gt; 591&lt;figure&gt;
369&lt;img src="https://u.peppe.rs/XM3.jpg" alt="The Lotus58 in action" /&gt;&lt;figcaption aria-hidden="true"&gt;The Lotus58 in action&lt;/figcaption&gt; 592&lt;img src="https://u.peppe.rs/XM3.jpg" alt="The Lotus58 in action" /&gt;
593&lt;figcaption aria-hidden="true"&gt;The Lotus58 in action&lt;/figcaption&gt;
370&lt;/figure&gt;</description> 594&lt;/figure&gt;</description>
371<link>https://peppe.rs/posts/lotus58/</link> 595<link>https://peppe.rs/posts/lotus58/</link>
372<pubDate>Mon, 13 Jun 2022 13:55:00 +0000</pubDate> 596<pubDate>Mon, 13 Jun 2022 13:55:00 +0000</pubDate>
@@ -374,14 +598,23 @@ H N E I
374</item> 598</item>
375<item> 599<item>
376<title>Lightweight Linting</title> 600<title>Lightweight Linting</title>
377<description>&lt;p&gt;&lt;a href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries"&gt;Tree-sitter&lt;/a&gt; queries allow you to search for patterns in syntax trees, much like a regex would, in text. Combine that with some Rust glue to write simple, custom linters.&lt;/p&gt; 601<description>&lt;p&gt;&lt;a
602href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries"&gt;Tree-sitter&lt;/a&gt;
603queries allow you to search for patterns in syntax trees, much like a
604regex would, in text. Combine that with some Rust glue to write simple,
605custom linters.&lt;/p&gt;
378&lt;h3 id="tree-sitter-syntax-trees"&gt;Tree-sitter syntax trees&lt;/h3&gt; 606&lt;h3 id="tree-sitter-syntax-trees"&gt;Tree-sitter syntax trees&lt;/h3&gt;
379&lt;p&gt;Here is a quick crash course on syntax trees generated by tree-sitter. Syntax trees produced by tree-sitter are represented by S-expressions. The generated S-expression for the following Rust code,&lt;/p&gt; 607&lt;p&gt;Here is a quick crash course on syntax trees generated by
380&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;fn&lt;/span&gt; main() &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt; 608tree-sitter. Syntax trees produced by tree-sitter are represented by
609S-expressions. The generated S-expression for the following Rust
610code,&lt;/p&gt;
611&lt;div class="sourceCode" id="cb1"&gt;&lt;pre
612class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;fn&lt;/span&gt; main() &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
381&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;let&lt;/span&gt; x &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;2&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 613&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;let&lt;/span&gt; x &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;2&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
382&lt;span id="cb1-3"&gt;&lt;a href="#cb1-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 614&lt;span id="cb1-3"&gt;&lt;a href="#cb1-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
383&lt;p&gt;would be:&lt;/p&gt; 615&lt;p&gt;would be:&lt;/p&gt;
384&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(source_file&lt;/span&gt; 616&lt;div class="sourceCode" id="cb2"&gt;&lt;pre
617class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(source_file&lt;/span&gt;
385&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (function_item&lt;/span&gt; 618&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (function_item&lt;/span&gt;
386&lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; name: (identifier)&lt;/span&gt; 619&lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; name: (identifier)&lt;/span&gt;
387&lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; parameters: (parameters)&lt;/span&gt; 620&lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; parameters: (parameters)&lt;/span&gt;
@@ -390,13 +623,19 @@ H N E I
390&lt;span id="cb2-7"&gt;&lt;a href="#cb2-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (let_declaration &lt;/span&gt; 623&lt;span id="cb2-7"&gt;&lt;a href="#cb2-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (let_declaration &lt;/span&gt;
391&lt;span id="cb2-8"&gt;&lt;a href="#cb2-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt; 624&lt;span id="cb2-8"&gt;&lt;a href="#cb2-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt;
392&lt;span id="cb2-9"&gt;&lt;a href="#cb2-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal)))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 625&lt;span id="cb2-9"&gt;&lt;a href="#cb2-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal)))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
393&lt;p&gt;Syntax trees generated by tree-sitter have a couple of other cool properties: they are &lt;em&gt;lossless&lt;/em&gt; syntax trees. Given a lossless syntax tree, you can regenerate the original source code in its entirety. Consider the following addition to our example:&lt;/p&gt; 626&lt;p&gt;Syntax trees generated by tree-sitter have a couple of other cool
394&lt;div class="sourceCode" id="cb3"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;fn&lt;/span&gt; main() &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt; 627properties: they are &lt;em&gt;lossless&lt;/em&gt; syntax trees. Given a lossless
628syntax tree, you can regenerate the original source code in its
629entirety. Consider the following addition to our example:&lt;/p&gt;
630&lt;div class="sourceCode" id="cb3"&gt;&lt;pre
631class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;fn&lt;/span&gt; main() &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
395&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="co"&gt;// a comment goes here&lt;/span&gt;&lt;/span&gt; 632&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="co"&gt;// a comment goes here&lt;/span&gt;&lt;/span&gt;
396&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;let&lt;/span&gt; x &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;2&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 633&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;let&lt;/span&gt; x &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="dv"&gt;2&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
397&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 634&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
398&lt;p&gt;The tree-sitter syntax tree preserves the comment, while the typical abstract syntax tree wouldn’t:&lt;/p&gt; 635&lt;p&gt;The tree-sitter syntax tree preserves the comment, while the typical
399&lt;div class="sourceCode" id="cb4"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (source_file&lt;/span&gt; 636abstract syntax tree wouldn’t:&lt;/p&gt;
637&lt;div class="sourceCode" id="cb4"&gt;&lt;pre
638class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (source_file&lt;/span&gt;
400&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (function_item&lt;/span&gt; 639&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (function_item&lt;/span&gt;
401&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; name: (identifier)&lt;/span&gt; 640&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; name: (identifier)&lt;/span&gt;
402&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; parameters: (parameters)&lt;/span&gt; 641&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; parameters: (parameters)&lt;/span&gt;
@@ -407,25 +646,34 @@ H N E I
407&lt;span id="cb4-9"&gt;&lt;a href="#cb4-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt; 646&lt;span id="cb4-9"&gt;&lt;a href="#cb4-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt;
408&lt;span id="cb4-10"&gt;&lt;a href="#cb4-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal)))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 647&lt;span id="cb4-10"&gt;&lt;a href="#cb4-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal)))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
409&lt;h3 id="tree-sitter-queries"&gt;Tree-sitter queries&lt;/h3&gt; 648&lt;h3 id="tree-sitter-queries"&gt;Tree-sitter queries&lt;/h3&gt;
410&lt;p&gt;Tree-sitter provides a DSL to match over CSTs. These queries resemble our S-expression syntax trees, here is a query to match all line comments in a Rust CST:&lt;/p&gt; 649&lt;p&gt;Tree-sitter provides a DSL to match over CSTs. These queries resemble
411&lt;div class="sourceCode" id="cb5"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb5-1"&gt;&lt;a href="#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(line_comment)&lt;/span&gt; 650our S-expression syntax trees, here is a query to match all line
651comments in a Rust CST:&lt;/p&gt;
652&lt;div class="sourceCode" id="cb5"&gt;&lt;pre
653class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb5-1"&gt;&lt;a href="#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(line_comment)&lt;/span&gt;
412&lt;span id="cb5-2"&gt;&lt;a href="#cb5-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 654&lt;span id="cb5-2"&gt;&lt;a href="#cb5-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
413&lt;span id="cb5-3"&gt;&lt;a href="#cb5-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; matches the following rust code&lt;/span&gt;&lt;/span&gt; 655&lt;span id="cb5-3"&gt;&lt;a href="#cb5-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; matches the following rust code&lt;/span&gt;&lt;/span&gt;
414&lt;span id="cb5-4"&gt;&lt;a href="#cb5-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; // a comment goes here&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 656&lt;span id="cb5-4"&gt;&lt;a href="#cb5-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; // a comment goes here&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
415&lt;p&gt;Neat, eh? But don’t take my word for it, give it a go on the &lt;a href="https://tree-sitter.github.io/tree-sitter/playground"&gt;tree-sitter playground&lt;/a&gt;. Type in a query like so:&lt;/p&gt; 657&lt;p&gt;Neat, eh? But don’t take my word for it, give it a go on the &lt;a
416&lt;div class="sourceCode" id="cb6"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb6-1"&gt;&lt;a href="#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; the web playground requires you to specify a &amp;quot;capture&amp;quot;&lt;/span&gt;&lt;/span&gt; 658href="https://tree-sitter.github.io/tree-sitter/playground"&gt;tree-sitter
659playground&lt;/a&gt;. Type in a query like so:&lt;/p&gt;
660&lt;div class="sourceCode" id="cb6"&gt;&lt;pre
661class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb6-1"&gt;&lt;a href="#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; the web playground requires you to specify a &amp;quot;capture&amp;quot;&lt;/span&gt;&lt;/span&gt;
417&lt;span id="cb6-2"&gt;&lt;a href="#cb6-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; you will notice the capture and the nodes it captured&lt;/span&gt;&lt;/span&gt; 662&lt;span id="cb6-2"&gt;&lt;a href="#cb6-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; you will notice the capture and the nodes it captured&lt;/span&gt;&lt;/span&gt;
418&lt;span id="cb6-3"&gt;&lt;a href="#cb6-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; turn blue&lt;/span&gt;&lt;/span&gt; 663&lt;span id="cb6-3"&gt;&lt;a href="#cb6-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; turn blue&lt;/span&gt;&lt;/span&gt;
419&lt;span id="cb6-4"&gt;&lt;a href="#cb6-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(line_comment) @capture&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 664&lt;span id="cb6-4"&gt;&lt;a href="#cb6-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(line_comment) @capture&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
420&lt;p&gt;Here’s another to match &lt;code&gt;let&lt;/code&gt; expressions that bind an integer to an identifier:&lt;/p&gt; 665&lt;p&gt;Here’s another to match &lt;code&gt;let&lt;/code&gt; expressions that bind an
421&lt;div class="sourceCode" id="cb7"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb7-1"&gt;&lt;a href="#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(let_declaration&lt;/span&gt; 666integer to an identifier:&lt;/p&gt;
667&lt;div class="sourceCode" id="cb7"&gt;&lt;pre
668class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb7-1"&gt;&lt;a href="#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(let_declaration&lt;/span&gt;
422&lt;span id="cb7-2"&gt;&lt;a href="#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt; 669&lt;span id="cb7-2"&gt;&lt;a href="#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt;
423&lt;span id="cb7-3"&gt;&lt;a href="#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt; 670&lt;span id="cb7-3"&gt;&lt;a href="#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt;
424&lt;span id="cb7-4"&gt;&lt;a href="#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt; 671&lt;span id="cb7-4"&gt;&lt;a href="#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt;
425&lt;span id="cb7-5"&gt;&lt;a href="#cb7-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; matches:&lt;/span&gt;&lt;/span&gt; 672&lt;span id="cb7-5"&gt;&lt;a href="#cb7-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; matches:&lt;/span&gt;&lt;/span&gt;
426&lt;span id="cb7-6"&gt;&lt;a href="#cb7-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = 2;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 673&lt;span id="cb7-6"&gt;&lt;a href="#cb7-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = 2;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
427&lt;p&gt;We can &lt;em&gt;capture&lt;/em&gt; nodes into variables:&lt;/p&gt; 674&lt;p&gt;We can &lt;em&gt;capture&lt;/em&gt; nodes into variables:&lt;/p&gt;
428&lt;div class="sourceCode" id="cb8"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb8-1"&gt;&lt;a href="#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(let_declaration &lt;/span&gt; 675&lt;div class="sourceCode" id="cb8"&gt;&lt;pre
676class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb8-1"&gt;&lt;a href="#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(let_declaration &lt;/span&gt;
429&lt;span id="cb8-2"&gt;&lt;a href="#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier) @my-capture&lt;/span&gt; 677&lt;span id="cb8-2"&gt;&lt;a href="#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier) @my-capture&lt;/span&gt;
430&lt;span id="cb8-3"&gt;&lt;a href="#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt; 678&lt;span id="cb8-3"&gt;&lt;a href="#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt;
431&lt;span id="cb8-4"&gt;&lt;a href="#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt; 679&lt;span id="cb8-4"&gt;&lt;a href="#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt;
@@ -435,7 +683,8 @@ H N E I
435&lt;span id="cb8-8"&gt;&lt;a href="#cb8-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; captures:&lt;/span&gt;&lt;/span&gt; 683&lt;span id="cb8-8"&gt;&lt;a href="#cb8-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; captures:&lt;/span&gt;&lt;/span&gt;
436&lt;span id="cb8-9"&gt;&lt;a href="#cb8-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; foo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 684&lt;span id="cb8-9"&gt;&lt;a href="#cb8-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; foo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
437&lt;p&gt;And apply certain &lt;em&gt;predicates&lt;/em&gt; to captures:&lt;/p&gt; 685&lt;p&gt;And apply certain &lt;em&gt;predicates&lt;/em&gt; to captures:&lt;/p&gt;
438&lt;div class="sourceCode" id="cb9"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb9-1"&gt;&lt;a href="#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((let_declaration&lt;/span&gt; 686&lt;div class="sourceCode" id="cb9"&gt;&lt;pre
687class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb9-1"&gt;&lt;a href="#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((let_declaration&lt;/span&gt;
439&lt;span id="cb9-2"&gt;&lt;a href="#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier) @my-capture&lt;/span&gt; 688&lt;span id="cb9-2"&gt;&lt;a href="#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier) @my-capture&lt;/span&gt;
440&lt;span id="cb9-3"&gt;&lt;a href="#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt; 689&lt;span id="cb9-3"&gt;&lt;a href="#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt;
441&lt;span id="cb9-4"&gt;&lt;a href="#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="sc"&gt;#e&lt;/span&gt;q? @my-capture &lt;span class="st"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;))&lt;/span&gt; 690&lt;span id="cb9-4"&gt;&lt;a href="#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="sc"&gt;#e&lt;/span&gt;q? @my-capture &lt;span class="st"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;))&lt;/span&gt;
@@ -445,8 +694,10 @@ H N E I
445&lt;span id="cb9-8"&gt;&lt;a href="#cb9-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 694&lt;span id="cb9-8"&gt;&lt;a href="#cb9-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
446&lt;span id="cb9-9"&gt;&lt;a href="#cb9-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; and not:&lt;/span&gt;&lt;/span&gt; 695&lt;span id="cb9-9"&gt;&lt;a href="#cb9-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; and not:&lt;/span&gt;&lt;/span&gt;
447&lt;span id="cb9-10"&gt;&lt;a href="#cb9-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let bar = 2;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 696&lt;span id="cb9-10"&gt;&lt;a href="#cb9-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let bar = 2;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
448&lt;p&gt;The &lt;code&gt;#match?&lt;/code&gt; predicate checks if a capture matches a regex:&lt;/p&gt; 697&lt;p&gt;The &lt;code&gt;#match?&lt;/code&gt; predicate checks if a capture matches a
449&lt;div class="sourceCode" id="cb10"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb10-1"&gt;&lt;a href="#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((let_declaration&lt;/span&gt; 698regex:&lt;/p&gt;
699&lt;div class="sourceCode" id="cb10"&gt;&lt;pre
700class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb10-1"&gt;&lt;a href="#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((let_declaration&lt;/span&gt;
450&lt;span id="cb10-2"&gt;&lt;a href="#cb10-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier) @my-capture&lt;/span&gt; 701&lt;span id="cb10-2"&gt;&lt;a href="#cb10-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier) @my-capture&lt;/span&gt;
451&lt;span id="cb10-3"&gt;&lt;a href="#cb10-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt; 702&lt;span id="cb10-3"&gt;&lt;a href="#cb10-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (integer_literal))&lt;/span&gt;
452&lt;span id="cb10-4"&gt;&lt;a href="#cb10-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @my-capture &lt;span class="st"&gt;&amp;quot;foo|bar&amp;quot;&lt;/span&gt;))&lt;/span&gt; 703&lt;span id="cb10-4"&gt;&lt;a href="#cb10-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @my-capture &lt;span class="st"&gt;&amp;quot;foo|bar&amp;quot;&lt;/span&gt;))&lt;/span&gt;
@@ -454,8 +705,10 @@ H N E I
454&lt;span id="cb10-6"&gt;&lt;a href="#cb10-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; matches both `foo` and `bar`:&lt;/span&gt;&lt;/span&gt; 705&lt;span id="cb10-6"&gt;&lt;a href="#cb10-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; matches both `foo` and `bar`:&lt;/span&gt;&lt;/span&gt;
455&lt;span id="cb10-7"&gt;&lt;a href="#cb10-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = 2;&lt;/span&gt;&lt;/span&gt; 706&lt;span id="cb10-7"&gt;&lt;a href="#cb10-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = 2;&lt;/span&gt;&lt;/span&gt;
456&lt;span id="cb10-8"&gt;&lt;a href="#cb10-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let bar = 2;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 707&lt;span id="cb10-8"&gt;&lt;a href="#cb10-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let bar = 2;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
457&lt;p&gt;Exhibit indifference, as a stoic programmer would, with the &lt;em&gt;wildcard&lt;/em&gt; pattern:&lt;/p&gt; 708&lt;p&gt;Exhibit indifference, as a stoic programmer would, with the
458&lt;div class="sourceCode" id="cb11"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb11-1"&gt;&lt;a href="#cb11-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(let_declaration&lt;/span&gt; 709&lt;em&gt;wildcard&lt;/em&gt; pattern:&lt;/p&gt;
710&lt;div class="sourceCode" id="cb11"&gt;&lt;pre
711class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb11-1"&gt;&lt;a href="#cb11-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(let_declaration&lt;/span&gt;
459&lt;span id="cb11-2"&gt;&lt;a href="#cb11-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt; 712&lt;span id="cb11-2"&gt;&lt;a href="#cb11-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; pattern: (identifier)&lt;/span&gt;
460&lt;span id="cb11-3"&gt;&lt;a href="#cb11-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (&lt;span class="op"&gt;_&lt;/span&gt;))&lt;/span&gt; 713&lt;span id="cb11-3"&gt;&lt;a href="#cb11-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; value: (&lt;span class="op"&gt;_&lt;/span&gt;))&lt;/span&gt;
461&lt;span id="cb11-4"&gt;&lt;a href="#cb11-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt; 714&lt;span id="cb11-4"&gt;&lt;a href="#cb11-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt;
@@ -463,73 +716,106 @@ H N E I
463&lt;span id="cb11-6"&gt;&lt;a href="#cb11-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = &amp;quot;foo&amp;quot;;&lt;/span&gt;&lt;/span&gt; 716&lt;span id="cb11-6"&gt;&lt;a href="#cb11-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = &amp;quot;foo&amp;quot;;&lt;/span&gt;&lt;/span&gt;
464&lt;span id="cb11-7"&gt;&lt;a href="#cb11-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = 42;&lt;/span&gt;&lt;/span&gt; 717&lt;span id="cb11-7"&gt;&lt;a href="#cb11-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = 42;&lt;/span&gt;&lt;/span&gt;
465&lt;span id="cb11-8"&gt;&lt;a href="#cb11-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = bar;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 718&lt;span id="cb11-8"&gt;&lt;a href="#cb11-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;; let foo = bar;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
466&lt;p&gt;&lt;a href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries"&gt;The documentation&lt;/a&gt; does the tree-sitter query DSL more justice, but we now know enough to write our first lint.&lt;/p&gt; 719&lt;p&gt;&lt;a
720href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries"&gt;The
721documentation&lt;/a&gt; does the tree-sitter query DSL more justice, but we
722now know enough to write our first lint.&lt;/p&gt;
467&lt;h3 id="write-you-a-tree-sitter-lint"&gt;Write you a tree-sitter lint&lt;/h3&gt; 723&lt;h3 id="write-you-a-tree-sitter-lint"&gt;Write you a tree-sitter lint&lt;/h3&gt;
468&lt;p&gt;Strings in &lt;code&gt;std::env&lt;/code&gt; functions are error prone:&lt;/p&gt; 724&lt;p&gt;Strings in &lt;code&gt;std::env&lt;/code&gt; functions are error prone:&lt;/p&gt;
469&lt;div class="sourceCode" id="cb12"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb12-1"&gt;&lt;a href="#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;std::env::&lt;/span&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTACE&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 725&lt;div class="sourceCode" id="cb12"&gt;&lt;pre
726class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb12-1"&gt;&lt;a href="#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;std::env::&lt;/span&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTACE&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
470&lt;span id="cb12-2"&gt;&lt;a href="#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// ^^^^ &amp;quot;TACE&amp;quot; instead of &amp;quot;TRACE&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 727&lt;span id="cb12-2"&gt;&lt;a href="#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// ^^^^ &amp;quot;TACE&amp;quot; instead of &amp;quot;TRACE&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
471&lt;p&gt;I prefer this instead:&lt;/p&gt; 728&lt;p&gt;I prefer this instead:&lt;/p&gt;
472&lt;div class="sourceCode" id="cb13"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb13-1"&gt;&lt;a href="#cb13-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// somewhere in a module that is well spellchecked&lt;/span&gt;&lt;/span&gt; 729&lt;div class="sourceCode" id="cb13"&gt;&lt;pre
730class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb13-1"&gt;&lt;a href="#cb13-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// somewhere in a module that is well spellchecked&lt;/span&gt;&lt;/span&gt;
473&lt;span id="cb13-2"&gt;&lt;a href="#cb13-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;static&lt;/span&gt; BACKTRACE&lt;span class="op"&gt;:&lt;/span&gt; &lt;span class="op"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dt"&gt;str&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 731&lt;span id="cb13-2"&gt;&lt;a href="#cb13-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;static&lt;/span&gt; BACKTRACE&lt;span class="op"&gt;:&lt;/span&gt; &lt;span class="op"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dt"&gt;str&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
474&lt;span id="cb13-3"&gt;&lt;a href="#cb13-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 732&lt;span id="cb13-3"&gt;&lt;a href="#cb13-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
475&lt;span id="cb13-4"&gt;&lt;a href="#cb13-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// rest of the codebase&lt;/span&gt;&lt;/span&gt; 733&lt;span id="cb13-4"&gt;&lt;a href="#cb13-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// rest of the codebase&lt;/span&gt;&lt;/span&gt;
476&lt;span id="cb13-5"&gt;&lt;a href="#cb13-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;std::env::&lt;/span&gt;remove_var(BACKTRACE)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 734&lt;span id="cb13-5"&gt;&lt;a href="#cb13-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;std::env::&lt;/span&gt;remove_var(BACKTRACE)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
477&lt;p&gt;Let’s write a lint to find &lt;code&gt;std::env&lt;/code&gt; functions that use strings. Put aside the effectiveness of this lint for the moment, and take a stab at writing a tree-sitter query. For reference, a function call like so:&lt;/p&gt; 735&lt;p&gt;Let’s write a lint to find &lt;code&gt;std::env&lt;/code&gt; functions that use
478&lt;div class="sourceCode" id="cb14"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb14-1"&gt;&lt;a href="#cb14-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 736strings. Put aside the effectiveness of this lint for the moment, and
737take a stab at writing a tree-sitter query. For reference, a function
738call like so:&lt;/p&gt;
739&lt;div class="sourceCode" id="cb14"&gt;&lt;pre
740class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb14-1"&gt;&lt;a href="#cb14-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
479&lt;p&gt;Produces the following S-expression:&lt;/p&gt; 741&lt;p&gt;Produces the following S-expression:&lt;/p&gt;
480&lt;div class="sourceCode" id="cb15"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb15-1"&gt;&lt;a href="#cb15-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(call_expression&lt;/span&gt; 742&lt;div class="sourceCode" id="cb15"&gt;&lt;pre
743class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb15-1"&gt;&lt;a href="#cb15-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(call_expression&lt;/span&gt;
481&lt;span id="cb15-2"&gt;&lt;a href="#cb15-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (identifier)&lt;/span&gt; 744&lt;span id="cb15-2"&gt;&lt;a href="#cb15-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (identifier)&lt;/span&gt;
482&lt;span id="cb15-3"&gt;&lt;a href="#cb15-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; arguments: (arguments (string_literal)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 745&lt;span id="cb15-3"&gt;&lt;a href="#cb15-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; arguments: (arguments (string_literal)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
483&lt;p&gt;We are definitely looking for a &lt;code&gt;call_expression&lt;/code&gt;:&lt;/p&gt; 746&lt;p&gt;We are definitely looking for a &lt;code&gt;call_expression&lt;/code&gt;:&lt;/p&gt;
484&lt;div class="sourceCode" id="cb16"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb16-1"&gt;&lt;a href="#cb16-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(call_expression) @raise&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 747&lt;div class="sourceCode" id="cb16"&gt;&lt;pre
485&lt;p&gt;Whose function name matches &lt;code&gt;std::env::var&lt;/code&gt; or &lt;code&gt;std::env::remove_var&lt;/code&gt; at the very least (I know, I know, this isn’t the most optimal regex):&lt;/p&gt; 748class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb16-1"&gt;&lt;a href="#cb16-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(call_expression) @raise&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
486&lt;div class="sourceCode" id="cb17"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb17-1"&gt;&lt;a href="#cb17-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt; 749&lt;p&gt;Whose function name matches &lt;code&gt;std::env::var&lt;/code&gt; or
750&lt;code&gt;std::env::remove_var&lt;/code&gt; at the very least (I know, I know,
751this isn’t the most optimal regex):&lt;/p&gt;
752&lt;div class="sourceCode" id="cb17"&gt;&lt;pre
753class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb17-1"&gt;&lt;a href="#cb17-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt;
487&lt;span id="cb17-2"&gt;&lt;a href="#cb17-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name) @raise&lt;/span&gt; 754&lt;span id="cb17-2"&gt;&lt;a href="#cb17-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name) @raise&lt;/span&gt;
488&lt;span id="cb17-3"&gt;&lt;a href="#cb17-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;std::env::(var|remove_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 755&lt;span id="cb17-3"&gt;&lt;a href="#cb17-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;std::env::(var|remove_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
489&lt;p&gt;Let’s turn that &lt;code&gt;std::&lt;/code&gt; prefix optional:&lt;/p&gt; 756&lt;p&gt;Let’s turn that &lt;code&gt;std::&lt;/code&gt; prefix optional:&lt;/p&gt;
490&lt;div class="sourceCode" id="cb18"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb18-1"&gt;&lt;a href="#cb18-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt; 757&lt;div class="sourceCode" id="cb18"&gt;&lt;pre
758class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb18-1"&gt;&lt;a href="#cb18-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt;
491&lt;span id="cb18-2"&gt;&lt;a href="#cb18-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name) @raise&lt;/span&gt; 759&lt;span id="cb18-2"&gt;&lt;a href="#cb18-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name) @raise&lt;/span&gt;
492&lt;span id="cb18-3"&gt;&lt;a href="#cb18-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;(std::|)env::(var|remove_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 760&lt;span id="cb18-3"&gt;&lt;a href="#cb18-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;(std::|)env::(var|remove_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
493&lt;p&gt;And ensure that &lt;code&gt;arguments&lt;/code&gt; is a string:&lt;/p&gt; 761&lt;p&gt;And ensure that &lt;code&gt;arguments&lt;/code&gt; is a string:&lt;/p&gt;
494&lt;div class="sourceCode" id="cb19"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb19-1"&gt;&lt;a href="#cb19-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt; 762&lt;div class="sourceCode" id="cb19"&gt;&lt;pre
763class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb19-1"&gt;&lt;a href="#cb19-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt;
495&lt;span id="cb19-2"&gt;&lt;a href="#cb19-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name&lt;/span&gt; 764&lt;span id="cb19-2"&gt;&lt;a href="#cb19-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name&lt;/span&gt;
496&lt;span id="cb19-3"&gt;&lt;a href="#cb19-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; arguments: (arguments (string_literal)))&lt;/span&gt; 765&lt;span id="cb19-3"&gt;&lt;a href="#cb19-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; arguments: (arguments (string_literal)))&lt;/span&gt;
497&lt;span id="cb19-4"&gt;&lt;a href="#cb19-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;(std::|)env::(var|remove_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 766&lt;span id="cb19-4"&gt;&lt;a href="#cb19-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;(std::|)env::(var|remove_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
498&lt;h3 id="running-our-linter"&gt;Running our linter&lt;/h3&gt; 767&lt;h3 id="running-our-linter"&gt;Running our linter&lt;/h3&gt;
499&lt;p&gt;We could always plug our query into the web playground, but let’s go a step further:&lt;/p&gt; 768&lt;p&gt;We could always plug our query into the web playground, but let’s go
500&lt;div class="sourceCode" id="cb20"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb20-1"&gt;&lt;a href="#cb20-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;cargo&lt;/span&gt; new &lt;span class="at"&gt;--bin&lt;/span&gt; toy-lint&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 769a step further:&lt;/p&gt;
501&lt;p&gt;Add &lt;code&gt;tree-sitter&lt;/code&gt; and &lt;code&gt;tree-sitter-rust&lt;/code&gt; to your dependencies:&lt;/p&gt; 770&lt;div class="sourceCode" id="cb20"&gt;&lt;pre
502&lt;div class="sourceCode" id="cb21"&gt;&lt;pre class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb21-1"&gt;&lt;a href="#cb21-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# within Cargo.toml&lt;/span&gt;&lt;/span&gt; 771class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb20-1"&gt;&lt;a href="#cb20-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;cargo&lt;/span&gt; new &lt;span class="at"&gt;--bin&lt;/span&gt; toy-lint&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
772&lt;p&gt;Add &lt;code&gt;tree-sitter&lt;/code&gt; and &lt;code&gt;tree-sitter-rust&lt;/code&gt; to
773your dependencies:&lt;/p&gt;
774&lt;div class="sourceCode" id="cb21"&gt;&lt;pre
775class="sourceCode toml"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb21-1"&gt;&lt;a href="#cb21-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# within Cargo.toml&lt;/span&gt;&lt;/span&gt;
503&lt;span id="cb21-2"&gt;&lt;a href="#cb21-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[&lt;/span&gt;&lt;span class="dt"&gt;dependencies&lt;/span&gt;&lt;span class="kw"&gt;]&lt;/span&gt;&lt;/span&gt; 776&lt;span id="cb21-2"&gt;&lt;a href="#cb21-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[&lt;/span&gt;&lt;span class="dt"&gt;dependencies&lt;/span&gt;&lt;span class="kw"&gt;]&lt;/span&gt;&lt;/span&gt;
504&lt;span id="cb21-3"&gt;&lt;a href="#cb21-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;tree-sitter&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;0.20&amp;quot;&lt;/span&gt;&lt;/span&gt; 777&lt;span id="cb21-3"&gt;&lt;a href="#cb21-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;tree-sitter&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;0.20&amp;quot;&lt;/span&gt;&lt;/span&gt;
505&lt;span id="cb21-4"&gt;&lt;a href="#cb21-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 778&lt;span id="cb21-4"&gt;&lt;a href="#cb21-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
506&lt;span id="cb21-5"&gt;&lt;a href="#cb21-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[&lt;/span&gt;&lt;span class="dt"&gt;dependencies&lt;/span&gt;&lt;span class="kw"&gt;.&lt;/span&gt;&lt;span class="dt"&gt;tree-sitter-rust&lt;/span&gt;&lt;span class="kw"&gt;]&lt;/span&gt;&lt;/span&gt; 779&lt;span id="cb21-5"&gt;&lt;a href="#cb21-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;[&lt;/span&gt;&lt;span class="dt"&gt;dependencies&lt;/span&gt;&lt;span class="kw"&gt;.&lt;/span&gt;&lt;span class="dt"&gt;tree-sitter-rust&lt;/span&gt;&lt;span class="kw"&gt;]&lt;/span&gt;&lt;/span&gt;
507&lt;span id="cb21-6"&gt;&lt;a href="#cb21-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;git&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;https://github.com/tree-sitter/tree-sitter-rust&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 780&lt;span id="cb21-6"&gt;&lt;a href="#cb21-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="dt"&gt;git&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;https://github.com/tree-sitter/tree-sitter-rust&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
508&lt;p&gt;Let’s load in some Rust code to work with. As &lt;a href="https://en.wikipedia.org/wiki/Self-reference"&gt;an ode to Gödel&lt;/a&gt; (G&lt;code&gt;ode&lt;/code&gt;l?), why not load in our linter itself:&lt;/p&gt; 781&lt;p&gt;Let’s load in some Rust code to work with. As &lt;a
509&lt;div class="sourceCode" id="cb22"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb22-1"&gt;&lt;a href="#cb22-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;fn&lt;/span&gt; main() &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt; 782href="https://en.wikipedia.org/wiki/Self-reference"&gt;an ode to Gödel&lt;/a&gt;
783(G&lt;code&gt;ode&lt;/code&gt;l?), why not load in our linter itself:&lt;/p&gt;
784&lt;div class="sourceCode" id="cb22"&gt;&lt;pre
785class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb22-1"&gt;&lt;a href="#cb22-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;fn&lt;/span&gt; main() &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
510&lt;span id="cb22-2"&gt;&lt;a href="#cb22-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;let&lt;/span&gt; src &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;include_str!&lt;/span&gt;(&lt;span class="st"&gt;&amp;quot;main.rs&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 786&lt;span id="cb22-2"&gt;&lt;a href="#cb22-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;let&lt;/span&gt; src &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;include_str!&lt;/span&gt;(&lt;span class="st"&gt;&amp;quot;main.rs&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
511&lt;span id="cb22-3"&gt;&lt;a href="#cb22-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 787&lt;span id="cb22-3"&gt;&lt;a href="#cb22-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
512&lt;p&gt;Most tree-sitter APIs require a reference to a &lt;code&gt;Language&lt;/code&gt; struct, we will be working with Rust if you haven’t already guessed:&lt;/p&gt; 788&lt;p&gt;Most tree-sitter APIs require a reference to a &lt;code&gt;Language&lt;/code&gt;
513&lt;div class="sourceCode" id="cb23"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb23-1"&gt;&lt;a href="#cb23-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;Language&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 789struct, we will be working with Rust if you haven’t already guessed:&lt;/p&gt;
790&lt;div class="sourceCode" id="cb23"&gt;&lt;pre
791class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb23-1"&gt;&lt;a href="#cb23-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;Language&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
514&lt;span id="cb23-2"&gt;&lt;a href="#cb23-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 792&lt;span id="cb23-2"&gt;&lt;a href="#cb23-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
515&lt;span id="cb23-3"&gt;&lt;a href="#cb23-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; rust_lang&lt;span class="op"&gt;:&lt;/span&gt; Language &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter_rust::&lt;/span&gt;language()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 793&lt;span id="cb23-3"&gt;&lt;a href="#cb23-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; rust_lang&lt;span class="op"&gt;:&lt;/span&gt; Language &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter_rust::&lt;/span&gt;language()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
516&lt;p&gt;Enough scaffolding, let’s parse some Rust:&lt;/p&gt; 794&lt;p&gt;Enough scaffolding, let’s parse some Rust:&lt;/p&gt;
517&lt;div class="sourceCode" id="cb24"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb24-1"&gt;&lt;a href="#cb24-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;Parser&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 795&lt;div class="sourceCode" id="cb24"&gt;&lt;pre
796class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb24-1"&gt;&lt;a href="#cb24-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;Parser&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
518&lt;span id="cb24-2"&gt;&lt;a href="#cb24-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 797&lt;span id="cb24-2"&gt;&lt;a href="#cb24-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
519&lt;span id="cb24-3"&gt;&lt;a href="#cb24-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; &lt;span class="kw"&gt;mut&lt;/span&gt; parser &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;Parser::&lt;/span&gt;new()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 798&lt;span id="cb24-3"&gt;&lt;a href="#cb24-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; &lt;span class="kw"&gt;mut&lt;/span&gt; parser &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;Parser::&lt;/span&gt;new()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
520&lt;span id="cb24-4"&gt;&lt;a href="#cb24-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;parser&lt;span class="op"&gt;.&lt;/span&gt;set_language(rust_lang)&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 799&lt;span id="cb24-4"&gt;&lt;a href="#cb24-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;parser&lt;span class="op"&gt;.&lt;/span&gt;set_language(rust_lang)&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
521&lt;span id="cb24-5"&gt;&lt;a href="#cb24-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 800&lt;span id="cb24-5"&gt;&lt;a href="#cb24-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
522&lt;span id="cb24-6"&gt;&lt;a href="#cb24-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; parse_tree &lt;span class="op"&gt;=&lt;/span&gt; parser&lt;span class="op"&gt;.&lt;/span&gt;parse(&lt;span class="op"&gt;&amp;amp;&lt;/span&gt;src&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="cn"&gt;None&lt;/span&gt;)&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 801&lt;span id="cb24-6"&gt;&lt;a href="#cb24-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; parse_tree &lt;span class="op"&gt;=&lt;/span&gt; parser&lt;span class="op"&gt;.&lt;/span&gt;parse(&lt;span class="op"&gt;&amp;amp;&lt;/span&gt;src&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="cn"&gt;None&lt;/span&gt;)&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
523&lt;p&gt;The second argument to &lt;code&gt;Parser::parse&lt;/code&gt; may be of interest. Tree-sitter has this cool feature that allows for quick reparsing of existing parse trees if they contain edits. If you do happen to want to reparse a source file, you can pass in the old tree:&lt;/p&gt; 802&lt;p&gt;The second argument to &lt;code&gt;Parser::parse&lt;/code&gt; may be of interest.
524&lt;div class="sourceCode" id="cb25"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb25-1"&gt;&lt;a href="#cb25-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// if you wish to reparse instead of parse&lt;/span&gt;&lt;/span&gt; 803Tree-sitter has this cool feature that allows for quick reparsing of
804existing parse trees if they contain edits. If you do happen to want to
805reparse a source file, you can pass in the old tree:&lt;/p&gt;
806&lt;div class="sourceCode" id="cb25"&gt;&lt;pre
807class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb25-1"&gt;&lt;a href="#cb25-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// if you wish to reparse instead of parse&lt;/span&gt;&lt;/span&gt;
525&lt;span id="cb25-2"&gt;&lt;a href="#cb25-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;old_tree&lt;span class="op"&gt;.&lt;/span&gt;edit(&lt;span class="co"&gt;/* redacted */&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 808&lt;span id="cb25-2"&gt;&lt;a href="#cb25-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;old_tree&lt;span class="op"&gt;.&lt;/span&gt;edit(&lt;span class="co"&gt;/* redacted */&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
526&lt;span id="cb25-3"&gt;&lt;a href="#cb25-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 809&lt;span id="cb25-3"&gt;&lt;a href="#cb25-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
527&lt;span id="cb25-4"&gt;&lt;a href="#cb25-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// generate shiny new reparsed tree&lt;/span&gt;&lt;/span&gt; 810&lt;span id="cb25-4"&gt;&lt;a href="#cb25-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// generate shiny new reparsed tree&lt;/span&gt;&lt;/span&gt;
528&lt;span id="cb25-5"&gt;&lt;a href="#cb25-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; new_tree &lt;span class="op"&gt;=&lt;/span&gt; parser&lt;span class="op"&gt;.&lt;/span&gt;parse(&lt;span class="op"&gt;&amp;amp;&lt;/span&gt;src&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="cn"&gt;Some&lt;/span&gt;(old_tree))&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 811&lt;span id="cb25-5"&gt;&lt;a href="#cb25-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; new_tree &lt;span class="op"&gt;=&lt;/span&gt; parser&lt;span class="op"&gt;.&lt;/span&gt;parse(&lt;span class="op"&gt;&amp;amp;&lt;/span&gt;src&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="cn"&gt;Some&lt;/span&gt;(old_tree))&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
529&lt;p&gt;Anyhow (&lt;a href="http://github.com/dtolnay/anyhow"&gt;hah!&lt;/a&gt;), now that we have a parse tree, we can inspect it:&lt;/p&gt; 812&lt;p&gt;Anyhow (&lt;a href="http://github.com/dtolnay/anyhow"&gt;hah!&lt;/a&gt;), now
530&lt;div class="sourceCode" id="cb26"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb26-1"&gt;&lt;a href="#cb26-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;println!&lt;/span&gt;(&lt;span class="st"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; parse_tree&lt;span class="op"&gt;.&lt;/span&gt;root_node()&lt;span class="op"&gt;.&lt;/span&gt;to_sexp())&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 813that we have a parse tree, we can inspect it:&lt;/p&gt;
814&lt;div class="sourceCode" id="cb26"&gt;&lt;pre
815class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb26-1"&gt;&lt;a href="#cb26-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;println!&lt;/span&gt;(&lt;span class="st"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; parse_tree&lt;span class="op"&gt;.&lt;/span&gt;root_node()&lt;span class="op"&gt;.&lt;/span&gt;to_sexp())&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
531&lt;p&gt;Or better yet, run a query on it:&lt;/p&gt; 816&lt;p&gt;Or better yet, run a query on it:&lt;/p&gt;
532&lt;div class="sourceCode" id="cb27"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb27-1"&gt;&lt;a href="#cb27-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;Query&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 817&lt;div class="sourceCode" id="cb27"&gt;&lt;pre
818class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb27-1"&gt;&lt;a href="#cb27-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;Query&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
533&lt;span id="cb27-2"&gt;&lt;a href="#cb27-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 819&lt;span id="cb27-2"&gt;&lt;a href="#cb27-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
534&lt;span id="cb27-3"&gt;&lt;a href="#cb27-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; query &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;Query::&lt;/span&gt;new(&lt;/span&gt; 820&lt;span id="cb27-3"&gt;&lt;a href="#cb27-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; query &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;Query::&lt;/span&gt;new(&lt;/span&gt;
535&lt;span id="cb27-4"&gt;&lt;a href="#cb27-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; rust_lang&lt;span class="op"&gt;,&lt;/span&gt;&lt;/span&gt; 821&lt;span id="cb27-4"&gt;&lt;a href="#cb27-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; rust_lang&lt;span class="op"&gt;,&lt;/span&gt;&lt;/span&gt;
@@ -541,8 +827,11 @@ H N E I
541&lt;span id="cb27-10"&gt;&lt;a href="#cb27-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="st"&gt; &amp;quot;#&lt;/span&gt;&lt;/span&gt; 827&lt;span id="cb27-10"&gt;&lt;a href="#cb27-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="st"&gt; &amp;quot;#&lt;/span&gt;&lt;/span&gt;
542&lt;span id="cb27-11"&gt;&lt;a href="#cb27-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt; 828&lt;span id="cb27-11"&gt;&lt;a href="#cb27-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;/span&gt;
543&lt;span id="cb27-12"&gt;&lt;a href="#cb27-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 829&lt;span id="cb27-12"&gt;&lt;a href="#cb27-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
544&lt;p&gt;A &lt;code&gt;QueryCursor&lt;/code&gt; is tree-sitter’s way of maintaining state as we iterate through the matches or captures produced by running a query on the parse tree. Observe:&lt;/p&gt; 830&lt;p&gt;A &lt;code&gt;QueryCursor&lt;/code&gt; is tree-sitter’s way of maintaining state
545&lt;div class="sourceCode" id="cb28"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb28-1"&gt;&lt;a href="#cb28-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;QueryCursor&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 831as we iterate through the matches or captures produced by running a
832query on the parse tree. Observe:&lt;/p&gt;
833&lt;div class="sourceCode" id="cb28"&gt;&lt;pre
834class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb28-1"&gt;&lt;a href="#cb28-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;use&lt;/span&gt; &lt;span class="pp"&gt;tree_sitter::&lt;/span&gt;QueryCursor&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
546&lt;span id="cb28-2"&gt;&lt;a href="#cb28-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 835&lt;span id="cb28-2"&gt;&lt;a href="#cb28-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
547&lt;span id="cb28-3"&gt;&lt;a href="#cb28-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; &lt;span class="kw"&gt;mut&lt;/span&gt; query_cursor &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;QueryCursor::&lt;/span&gt;new()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 836&lt;span id="cb28-3"&gt;&lt;a href="#cb28-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; &lt;span class="kw"&gt;mut&lt;/span&gt; query_cursor &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="pp"&gt;QueryCursor::&lt;/span&gt;new()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
548&lt;span id="cb28-4"&gt;&lt;a href="#cb28-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; all_matches &lt;span class="op"&gt;=&lt;/span&gt; query_cursor&lt;span class="op"&gt;.&lt;/span&gt;matches(&lt;/span&gt; 837&lt;span id="cb28-4"&gt;&lt;a href="#cb28-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; all_matches &lt;span class="op"&gt;=&lt;/span&gt; query_cursor&lt;span class="op"&gt;.&lt;/span&gt;matches(&lt;/span&gt;
@@ -550,15 +839,22 @@ H N E I
550&lt;span id="cb28-6"&gt;&lt;a href="#cb28-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; parse_tree&lt;span class="op"&gt;.&lt;/span&gt;root_node()&lt;span class="op"&gt;,&lt;/span&gt;&lt;/span&gt; 839&lt;span id="cb28-6"&gt;&lt;a href="#cb28-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; parse_tree&lt;span class="op"&gt;.&lt;/span&gt;root_node()&lt;span class="op"&gt;,&lt;/span&gt;&lt;/span&gt;
551&lt;span id="cb28-7"&gt;&lt;a href="#cb28-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; src&lt;span class="op"&gt;.&lt;/span&gt;as_bytes()&lt;span class="op"&gt;,&lt;/span&gt;&lt;/span&gt; 840&lt;span id="cb28-7"&gt;&lt;a href="#cb28-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; src&lt;span class="op"&gt;.&lt;/span&gt;as_bytes()&lt;span class="op"&gt;,&lt;/span&gt;&lt;/span&gt;
552&lt;span id="cb28-8"&gt;&lt;a href="#cb28-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 841&lt;span id="cb28-8"&gt;&lt;a href="#cb28-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
553&lt;p&gt;We begin by passing our query to the cursor, followed by the “root node”, which is another way of saying, “start from the top”, and lastly, the source itself. If you have already taken a look at the C API, you will notice that the last argument, the source (known as the &lt;code&gt;TextProvider&lt;/code&gt;), is not required. The Rust bindings seem to require this argument to provide predicate functionality such as &lt;code&gt;#match?&lt;/code&gt; and &lt;code&gt;#eq?&lt;/code&gt;.&lt;/p&gt; 842&lt;p&gt;We begin by passing our query to the cursor, followed by the “root
843node”, which is another way of saying, “start from the top”, and lastly,
844the source itself. If you have already taken a look at the C API, you
845will notice that the last argument, the source (known as the
846&lt;code&gt;TextProvider&lt;/code&gt;), is not required. The Rust bindings seem to
847require this argument to provide predicate functionality such as
848&lt;code&gt;#match?&lt;/code&gt; and &lt;code&gt;#eq?&lt;/code&gt;.&lt;/p&gt;
554&lt;p&gt;Do something with the matches:&lt;/p&gt; 849&lt;p&gt;Do something with the matches:&lt;/p&gt;
555&lt;div class="sourceCode" id="cb29"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb29-1"&gt;&lt;a href="#cb29-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// get the index of the capture named &amp;quot;raise&amp;quot;&lt;/span&gt;&lt;/span&gt; 850&lt;div class="sourceCode" id="cb29"&gt;&lt;pre
851class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb29-1"&gt;&lt;a href="#cb29-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// get the index of the capture named &amp;quot;raise&amp;quot;&lt;/span&gt;&lt;/span&gt;
556&lt;span id="cb29-2"&gt;&lt;a href="#cb29-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; raise_idx &lt;span class="op"&gt;=&lt;/span&gt; query&lt;span class="op"&gt;.&lt;/span&gt;capture_index_for_name(&lt;span class="st"&gt;&amp;quot;raise&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 852&lt;span id="cb29-2"&gt;&lt;a href="#cb29-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; raise_idx &lt;span class="op"&gt;=&lt;/span&gt; query&lt;span class="op"&gt;.&lt;/span&gt;capture_index_for_name(&lt;span class="st"&gt;&amp;quot;raise&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;.&lt;/span&gt;unwrap()&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
557&lt;span id="cb29-3"&gt;&lt;a href="#cb29-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 853&lt;span id="cb29-3"&gt;&lt;a href="#cb29-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
558&lt;span id="cb29-4"&gt;&lt;a href="#cb29-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;for&lt;/span&gt; each_match &lt;span class="kw"&gt;in&lt;/span&gt; all_matches &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt; 854&lt;span id="cb29-4"&gt;&lt;a href="#cb29-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;for&lt;/span&gt; each_match &lt;span class="kw"&gt;in&lt;/span&gt; all_matches &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
559&lt;span id="cb29-5"&gt;&lt;a href="#cb29-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// iterate over all captures called &amp;quot;raise&amp;quot;&lt;/span&gt;&lt;/span&gt; 855&lt;span id="cb29-5"&gt;&lt;a href="#cb29-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// iterate over all captures called &amp;quot;raise&amp;quot;&lt;/span&gt;&lt;/span&gt;
560&lt;span id="cb29-6"&gt;&lt;a href="#cb29-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// ignore captures such as &amp;quot;fn-name&amp;quot;&lt;/span&gt;&lt;/span&gt; 856&lt;span id="cb29-6"&gt;&lt;a href="#cb29-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// ignore captures such as &amp;quot;fn-name&amp;quot;&lt;/span&gt;&lt;/span&gt;
561&lt;span id="cb29-7"&gt;&lt;a href="#cb29-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;for&lt;/span&gt; capture &lt;span class="kw"&gt;in&lt;/span&gt; each_match&lt;/span&gt; 857&lt;span id="cb29-7"&gt;&lt;a href="#cb29-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="cf"&gt;for&lt;/span&gt; capture &lt;span class="kw"&gt;in&lt;/span&gt; each_match&lt;/span&gt;
562&lt;span id="cb29-8"&gt;&lt;a href="#cb29-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;captures&lt;/span&gt; 858&lt;span id="cb29-8"&gt;&lt;a href="#cb29-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;captures&lt;/span&gt;
563&lt;span id="cb29-9"&gt;&lt;a href="#cb29-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;iter()&lt;/span&gt; 859&lt;span id="cb29-9"&gt;&lt;a href="#cb29-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;iter()&lt;/span&gt;
564&lt;span id="cb29-10"&gt;&lt;a href="#cb29-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;filter(&lt;span class="op"&gt;|&lt;/span&gt;c&lt;span class="op"&gt;|&lt;/span&gt; c&lt;span class="op"&gt;.&lt;/span&gt;idx &lt;span class="op"&gt;==&lt;/span&gt; raise_idx)&lt;/span&gt; 860&lt;span id="cb29-10"&gt;&lt;a href="#cb29-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;filter(&lt;span class="op"&gt;|&lt;/span&gt;c&lt;span class="op"&gt;|&lt;/span&gt; c&lt;span class="op"&gt;.&lt;/span&gt;idx &lt;span class="op"&gt;==&lt;/span&gt; raise_idx)&lt;/span&gt;
@@ -573,8 +869,10 @@ H N E I
573&lt;span id="cb29-19"&gt;&lt;a href="#cb29-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; )&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 869&lt;span id="cb29-19"&gt;&lt;a href="#cb29-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; )&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
574&lt;span id="cb29-20"&gt;&lt;a href="#cb29-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt; 870&lt;span id="cb29-20"&gt;&lt;a href="#cb29-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;
575&lt;span id="cb29-21"&gt;&lt;a href="#cb29-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 871&lt;span id="cb29-21"&gt;&lt;a href="#cb29-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
576&lt;p&gt;Lastly, add the following line to your source code, to get the linter to catch something:&lt;/p&gt; 872&lt;p&gt;Lastly, add the following line to your source code, to get the linter
577&lt;div class="sourceCode" id="cb30"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb30-1"&gt;&lt;a href="#cb30-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;env::&lt;/span&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 873to catch something:&lt;/p&gt;
874&lt;div class="sourceCode" id="cb30"&gt;&lt;pre
875class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb30-1"&gt;&lt;a href="#cb30-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;env::&lt;/span&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
578&lt;p&gt;And &lt;code&gt;cargo run&lt;/code&gt;:&lt;/p&gt; 876&lt;p&gt;And &lt;code&gt;cargo run&lt;/code&gt;:&lt;/p&gt;
579&lt;pre class="shell"&gt;&lt;code&gt;λ cargo run 877&lt;pre class="shell"&gt;&lt;code&gt;λ cargo run
580 Compiling toy-lint v0.1.0 (/redacted/path/to/toy-lint) 878 Compiling toy-lint v0.1.0 (/redacted/path/to/toy-lint)
@@ -583,17 +881,33 @@ H N E I
583[Line: 40, Col: 4] Offending source code: `env::remove_var(&amp;quot;RUST_BACKTRACE&amp;quot;)`&lt;/code&gt;&lt;/pre&gt; 881[Line: 40, Col: 4] Offending source code: `env::remove_var(&amp;quot;RUST_BACKTRACE&amp;quot;)`&lt;/code&gt;&lt;/pre&gt;
584&lt;p&gt;Thank you tree-sitter!&lt;/p&gt; 882&lt;p&gt;Thank you tree-sitter!&lt;/p&gt;
585&lt;h3 id="bonus"&gt;Bonus&lt;/h3&gt; 883&lt;h3 id="bonus"&gt;Bonus&lt;/h3&gt;
586&lt;p&gt;Keen readers will notice that I avoided &lt;code&gt;std::env::set_var&lt;/code&gt;. Because &lt;code&gt;set_var&lt;/code&gt; is called with two arguments, a “key” and a “value”, unlike &lt;code&gt;env::var&lt;/code&gt; and &lt;code&gt;env::remove_var&lt;/code&gt;. As a result, it requires more juggling:&lt;/p&gt; 884&lt;p&gt;Keen readers will notice that I avoided
587&lt;div class="sourceCode" id="cb32"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb32-1"&gt;&lt;a href="#cb32-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt; 885&lt;code&gt;std::env::set_var&lt;/code&gt;. Because &lt;code&gt;set_var&lt;/code&gt; is called
886with two arguments, a “key” and a “value”, unlike &lt;code&gt;env::var&lt;/code&gt;
887and &lt;code&gt;env::remove_var&lt;/code&gt;. As a result, it requires more
888juggling:&lt;/p&gt;
889&lt;div class="sourceCode" id="cb32"&gt;&lt;pre
890class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb32-1"&gt;&lt;a href="#cb32-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((call_expression&lt;/span&gt;
588&lt;span id="cb32-2"&gt;&lt;a href="#cb32-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name&lt;/span&gt; 891&lt;span id="cb32-2"&gt;&lt;a href="#cb32-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; function: (&lt;span class="op"&gt;_&lt;/span&gt;) @fn-name&lt;/span&gt;
589&lt;span id="cb32-3"&gt;&lt;a href="#cb32-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; arguments: (arguments &lt;span class="op"&gt;.&lt;/span&gt; (string_literal)&lt;span class="op"&gt;?&lt;/span&gt; &lt;span class="op"&gt;.&lt;/span&gt; (string_literal) &lt;span class="op"&gt;.&lt;/span&gt;)) @raise&lt;/span&gt; 892&lt;span id="cb32-3"&gt;&lt;a href="#cb32-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; arguments: (arguments &lt;span class="op"&gt;.&lt;/span&gt; (string_literal)&lt;span class="op"&gt;?&lt;/span&gt; &lt;span class="op"&gt;.&lt;/span&gt; (string_literal) &lt;span class="op"&gt;.&lt;/span&gt;)) @raise&lt;/span&gt;
590&lt;span id="cb32-4"&gt;&lt;a href="#cb32-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;(std::|)env::(var|remove_var|set_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 893&lt;span id="cb32-4"&gt;&lt;a href="#cb32-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (#match? @fn-name &lt;span class="st"&gt;&amp;quot;(std::|)env::(var|remove_var|set_var)&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
591&lt;p&gt;The interesting part of this query is the humble &lt;code&gt;.&lt;/code&gt;, the &lt;em&gt;anchor&lt;/em&gt; operator. Anchors help constrain child nodes in certain ways. In this case, it ensures that we match exactly two &lt;code&gt;string_literal&lt;/code&gt;s who are siblings or exactly one &lt;code&gt;string_literal&lt;/code&gt; with no siblings. Unfortunately, this query also matches the following invalid Rust code:&lt;/p&gt; 894&lt;p&gt;The interesting part of this query is the humble &lt;code&gt;.&lt;/code&gt;, the
592&lt;div class="sourceCode" id="cb33"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb33-1"&gt;&lt;a href="#cb33-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// remove_var accepts only 1 arg!&lt;/span&gt;&lt;/span&gt; 895&lt;em&gt;anchor&lt;/em&gt; operator. Anchors help constrain child nodes in certain
896ways. In this case, it ensures that we match exactly two
897&lt;code&gt;string_literal&lt;/code&gt;s who are siblings or exactly one
898&lt;code&gt;string_literal&lt;/code&gt; with no siblings. Unfortunately, this query
899also matches the following invalid Rust code:&lt;/p&gt;
900&lt;div class="sourceCode" id="cb33"&gt;&lt;pre
901class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb33-1"&gt;&lt;a href="#cb33-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// remove_var accepts only 1 arg!&lt;/span&gt;&lt;/span&gt;
593&lt;span id="cb33-2"&gt;&lt;a href="#cb33-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;std::env::&lt;/span&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 902&lt;span id="cb33-2"&gt;&lt;a href="#cb33-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="pp"&gt;std::env::&lt;/span&gt;remove_var(&lt;span class="st"&gt;&amp;quot;RUST_BACKTRACE&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
594&lt;h3 id="notes"&gt;Notes&lt;/h3&gt; 903&lt;h3 id="notes"&gt;Notes&lt;/h3&gt;
595&lt;p&gt;All-in-all, the query DSL does a great job in lowering the bar to writing language tools. The knowledge gained from mastering the query DSL can be applied to other languages that have tree-sitter grammars too. This query detects &lt;code&gt;to_json&lt;/code&gt; methods that do not accept additional arguments, in Ruby:&lt;/p&gt; 904&lt;p&gt;All-in-all, the query DSL does a great job in lowering the bar to
596&lt;div class="sourceCode" id="cb34"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb34-1"&gt;&lt;a href="#cb34-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((method&lt;/span&gt; 905writing language tools. The knowledge gained from mastering the query
906DSL can be applied to other languages that have tree-sitter grammars
907too. This query detects &lt;code&gt;to_json&lt;/code&gt; methods that do not accept
908additional arguments, in Ruby:&lt;/p&gt;
909&lt;div class="sourceCode" id="cb34"&gt;&lt;pre
910class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb34-1"&gt;&lt;a href="#cb34-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;((method&lt;/span&gt;
597&lt;span id="cb34-2"&gt;&lt;a href="#cb34-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; name: (identifier) @fn&lt;/span&gt; 911&lt;span id="cb34-2"&gt;&lt;a href="#cb34-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; name: (identifier) @fn&lt;/span&gt;
598&lt;span id="cb34-3"&gt;&lt;a href="#cb34-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; !parameters)&lt;/span&gt; 912&lt;span id="cb34-3"&gt;&lt;a href="#cb34-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; !parameters)&lt;/span&gt;
599&lt;span id="cb34-4"&gt;&lt;a href="#cb34-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="sc"&gt;#i&lt;/span&gt;s? @fn &lt;span class="st"&gt;&amp;quot;to_json&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description> 913&lt;span id="cb34-4"&gt;&lt;a href="#cb34-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="sc"&gt;#i&lt;/span&gt;s? @fn &lt;span class="st"&gt;&amp;quot;to_json&amp;quot;&lt;/span&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
@@ -603,13 +917,25 @@ H N E I
603</item> 917</item>
604<item> 918<item>
605<title>Novice Nix: Flake Templates</title> 919<title>Novice Nix: Flake Templates</title>
606<description>&lt;p&gt;Flakes are very handy to setup entirely pure, project-specific dependencies (not just dependencies, but build steps, shell environments and more) in a declarative way. Writing Flake expressions can get repetitive though, oftentimes, you’d much rather start off with a skeleton. Luckily, &lt;code&gt;nix&lt;/code&gt; already supports templates!&lt;/p&gt; 920<description>&lt;p&gt;Flakes are very handy to setup entirely pure, project-specific
607&lt;p&gt;You might already be familiar with &lt;code&gt;nix flake init&lt;/code&gt;, that drops a “default” flake expression into your current working directory. If you head over to the manpage:&lt;/p&gt; 921dependencies (not just dependencies, but build steps, shell environments
608&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;nix&lt;/span&gt; flake init &lt;span class="at"&gt;--help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 922and more) in a declarative way. Writing Flake expressions can get
609&lt;p&gt;You will read that &lt;code&gt;nix flake init&lt;/code&gt; creates a flake using the “default template”. Additionally, you can create a flake from a specific template by passing the &lt;code&gt;-t&lt;/code&gt; flag. Where does this default originate from?&lt;/p&gt; 923repetitive though, oftentimes, you’d much rather start off with a
924skeleton. Luckily, &lt;code&gt;nix&lt;/code&gt; already supports templates!&lt;/p&gt;
925&lt;p&gt;You might already be familiar with &lt;code&gt;nix flake init&lt;/code&gt;, that
926drops a “default” flake expression into your current working directory.
927If you head over to the manpage:&lt;/p&gt;
928&lt;div class="sourceCode" id="cb1"&gt;&lt;pre
929class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;nix&lt;/span&gt; flake init &lt;span class="at"&gt;--help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
930&lt;p&gt;You will read that &lt;code&gt;nix flake init&lt;/code&gt; creates a flake using
931the “default template”. Additionally, you can create a flake from a
932specific template by passing the &lt;code&gt;-t&lt;/code&gt; flag. Where does this
933default originate from?&lt;/p&gt;
610&lt;h2 id="flake-registries"&gt;Flake Registries&lt;/h2&gt; 934&lt;h2 id="flake-registries"&gt;Flake Registries&lt;/h2&gt;
611&lt;p&gt;Quick detour into registries! Registries are a way to alias popular flakes using identifiers:&lt;/p&gt; 935&lt;p&gt;Quick detour into registries! Registries are a way to alias popular
612&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# list a few predefined registries&lt;/span&gt;&lt;/span&gt; 936flakes using identifiers:&lt;/p&gt;
937&lt;div class="sourceCode" id="cb2"&gt;&lt;pre
938class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# list a few predefined registries&lt;/span&gt;&lt;/span&gt;
613&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix registry list&lt;/span&gt; 939&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix registry list&lt;/span&gt;
614&lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt; . . &lt;/span&gt; 940&lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt; . . &lt;/span&gt;
615&lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;global&lt;/span&gt; flake:nixpkgs github:NixOS/nixpkgs&lt;/span&gt; 941&lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;global&lt;/span&gt; flake:nixpkgs github:NixOS/nixpkgs&lt;/span&gt;
@@ -627,8 +953,11 @@ H N E I
627&lt;span id="cb2-16"&gt;&lt;a href="#cb2-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 953&lt;span id="cb2-16"&gt;&lt;a href="#cb2-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
628&lt;span id="cb2-17"&gt;&lt;a href="#cb2-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# which is short for&lt;/span&gt;&lt;/span&gt; 954&lt;span id="cb2-17"&gt;&lt;a href="#cb2-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# which is short for&lt;/span&gt;&lt;/span&gt;
629&lt;span id="cb2-18"&gt;&lt;a href="#cb2-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake show git+https://github.com/tweag/nickel&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 955&lt;span id="cb2-18"&gt;&lt;a href="#cb2-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake show git+https://github.com/tweag/nickel&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
630&lt;p&gt;You might notice a registry called &lt;code&gt;templates&lt;/code&gt; aliased to &lt;code&gt;github:NixOS/templates&lt;/code&gt;. Take a peek with &lt;code&gt;nix flake show&lt;/code&gt;:&lt;/p&gt; 956&lt;p&gt;You might notice a registry called &lt;code&gt;templates&lt;/code&gt; aliased to
631&lt;div class="sourceCode" id="cb3"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake show templates&lt;/span&gt; 957&lt;code&gt;github:NixOS/templates&lt;/code&gt;. Take a peek with
958&lt;code&gt;nix flake show&lt;/code&gt;:&lt;/p&gt;
959&lt;div class="sourceCode" id="cb3"&gt;&lt;pre
960class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake show templates&lt;/span&gt;
632&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;github:NixOS/templates/79f48a7b822f35c068c5e235da2e9fbd154cecee&lt;/span&gt;&lt;/span&gt; 961&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;github:NixOS/templates/79f48a7b822f35c068c5e235da2e9fbd154cecee&lt;/span&gt;&lt;/span&gt;
633&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├───defaultTemplate:&lt;/span&gt; template: A very basic flake&lt;/span&gt; 962&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├───defaultTemplate:&lt;/span&gt; template: A very basic flake&lt;/span&gt;
634&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;└───templates&lt;/span&gt;&lt;/span&gt; 963&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;└───templates&lt;/span&gt;&lt;/span&gt;
@@ -637,8 +966,12 @@ H N E I
637&lt;span id="cb3-7"&gt;&lt;a href="#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;├───rust-web-server:&lt;/span&gt; template: A Rust web server including a NixOS module&lt;/span&gt; 966&lt;span id="cb3-7"&gt;&lt;a href="#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;├───rust-web-server:&lt;/span&gt; template: A Rust web server including a NixOS module&lt;/span&gt;
638&lt;span id="cb3-8"&gt;&lt;a href="#cb3-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;├───simpleContainer:&lt;/span&gt; template: A NixOS container running apache-httpd&lt;/span&gt; 967&lt;span id="cb3-8"&gt;&lt;a href="#cb3-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;├───simpleContainer:&lt;/span&gt; template: A NixOS container running apache-httpd&lt;/span&gt;
639&lt;span id="cb3-9"&gt;&lt;a href="#cb3-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└───trivial:&lt;/span&gt; template: A very basic flake&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 968&lt;span id="cb3-9"&gt;&lt;a href="#cb3-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└───trivial:&lt;/span&gt; template: A very basic flake&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
640&lt;p&gt;Aha! There is a flake output called &lt;code&gt;defaultTemplate&lt;/code&gt;. This is the template being sourced when you run &lt;code&gt;nix flake init&lt;/code&gt;. Astute readers may conclude the following:&lt;/p&gt; 969&lt;p&gt;Aha! There is a flake output called &lt;code&gt;defaultTemplate&lt;/code&gt;.
641&lt;div class="sourceCode" id="cb4"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init&lt;/span&gt; 970This is the template being sourced when you run
971&lt;code&gt;nix flake init&lt;/code&gt;. Astute readers may conclude the
972following:&lt;/p&gt;
973&lt;div class="sourceCode" id="cb4"&gt;&lt;pre
974class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init&lt;/span&gt;
642&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 975&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
643&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# is equivalent to&lt;/span&gt;&lt;/span&gt; 976&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# is equivalent to&lt;/span&gt;&lt;/span&gt;
644&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; templates#defaultTemplate&lt;/span&gt; 977&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; templates#defaultTemplate&lt;/span&gt;
@@ -649,7 +982,8 @@ H N E I
649&lt;span id="cb4-9"&gt;&lt;a href="#cb4-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# which is short for&lt;/span&gt;&lt;/span&gt; 982&lt;span id="cb4-9"&gt;&lt;a href="#cb4-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# which is short for&lt;/span&gt;&lt;/span&gt;
650&lt;span id="cb4-10"&gt;&lt;a href="#cb4-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; git+https://github.com/NixOS/templates#defaultTemplate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 983&lt;span id="cb4-10"&gt;&lt;a href="#cb4-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; git+https://github.com/NixOS/templates#defaultTemplate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
651&lt;p&gt;Similarly, the other templates can be accessed via:&lt;/p&gt; 984&lt;p&gt;Similarly, the other templates can be accessed via:&lt;/p&gt;
652&lt;div class="sourceCode" id="cb5"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb5-1"&gt;&lt;a href="#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; templates#c-hello&lt;/span&gt; 985&lt;div class="sourceCode" id="cb5"&gt;&lt;pre
986class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb5-1"&gt;&lt;a href="#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; templates#c-hello&lt;/span&gt;
653&lt;span id="cb5-2"&gt;&lt;a href="#cb5-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; templates#simpleContainer&lt;/span&gt; 987&lt;span id="cb5-2"&gt;&lt;a href="#cb5-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; templates#simpleContainer&lt;/span&gt;
654&lt;span id="cb5-3"&gt;&lt;a href="#cb5-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# I think you get the drift ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 988&lt;span id="cb5-3"&gt;&lt;a href="#cb5-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# I think you get the drift ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
655&lt;h2 id="rolling-your-own-templates"&gt;Rolling your own templates&lt;/h2&gt; 989&lt;h2 id="rolling-your-own-templates"&gt;Rolling your own templates&lt;/h2&gt;
@@ -657,38 +991,52 @@ H N E I
657&lt;ul&gt; 991&lt;ul&gt;
658&lt;li&gt;create a flake with a &lt;code&gt;templates&lt;/code&gt; output&lt;/li&gt; 992&lt;li&gt;create a flake with a &lt;code&gt;templates&lt;/code&gt; output&lt;/li&gt;
659&lt;li&gt;populate our template directories with content&lt;/li&gt; 993&lt;li&gt;populate our template directories with content&lt;/li&gt;
660&lt;li&gt;(&lt;strong&gt;optionally&lt;/strong&gt;) alias our custom templates flake to an identifier using registries, for easier access&lt;/li&gt; 994&lt;li&gt;(&lt;strong&gt;optionally&lt;/strong&gt;) alias our custom templates flake to an
995identifier using registries, for easier access&lt;/li&gt;
661&lt;/ul&gt; 996&lt;/ul&gt;
662&lt;p&gt;Start off by creating a directory to store your templates in (we will be converting this to a registry later):&lt;/p&gt; 997&lt;p&gt;Start off by creating a directory to store your templates in (we will
663&lt;div class="sourceCode" id="cb6"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb6-1"&gt;&lt;a href="#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; mkdir ~/mytemplates&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 998be converting this to a registry later):&lt;/p&gt;
664&lt;p&gt;A flake that exposes a “template” as its output looks something like this:&lt;/p&gt; 999&lt;div class="sourceCode" id="cb6"&gt;&lt;pre
665&lt;div class="sourceCode" id="cb7"&gt;&lt;pre class="sourceCode nix"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb7-1"&gt;&lt;a href="#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# inside ~/mytemplates/flake.nix&lt;/span&gt;&lt;/span&gt; 1000class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb6-1"&gt;&lt;a href="#cb6-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; mkdir ~/mytemplates&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
1001&lt;p&gt;A flake that exposes a “template” as its output looks something like
1002this:&lt;/p&gt;
1003&lt;div class="sourceCode" id="cb7"&gt;&lt;pre
1004class="sourceCode nix"&gt;&lt;code class="sourceCode nix"&gt;&lt;span id="cb7-1"&gt;&lt;a href="#cb7-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# inside ~/mytemplates/flake.nix&lt;/span&gt;&lt;/span&gt;
666&lt;span id="cb7-2"&gt;&lt;a href="#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 1005&lt;span id="cb7-2"&gt;&lt;a href="#cb7-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
667&lt;span id="cb7-3"&gt;&lt;a href="#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt; 1006&lt;span id="cb7-3"&gt;&lt;a href="#cb7-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
668&lt;span id="cb7-4"&gt;&lt;a href="#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;description&lt;/span&gt; = &lt;span class="st"&gt;&amp;quot;Pepper&amp;#39;s flake templates&amp;quot;&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1007&lt;span id="cb7-4"&gt;&lt;a href="#cb7-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;description&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;Pepper&amp;#39;s flake templates&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
669&lt;span id="cb7-5"&gt;&lt;a href="#cb7-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 1008&lt;span id="cb7-5"&gt;&lt;a href="#cb7-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
670&lt;span id="cb7-6"&gt;&lt;a href="#cb7-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;outputs&lt;/span&gt; = { self, ... }: {&lt;/span&gt; 1009&lt;span id="cb7-6"&gt;&lt;a href="#cb7-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;outputs&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt; &lt;span class="va"&gt;self&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="op"&gt;...&lt;/span&gt; &lt;span class="op"&gt;}&lt;/span&gt;: &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
671&lt;span id="cb7-7"&gt;&lt;a href="#cb7-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;templates&lt;/span&gt; = {&lt;/span&gt; 1010&lt;span id="cb7-7"&gt;&lt;a href="#cb7-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;templates&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
672&lt;span id="cb7-8"&gt;&lt;a href="#cb7-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;latex-report&lt;/span&gt; = {&lt;/span&gt; 1011&lt;span id="cb7-8"&gt;&lt;a href="#cb7-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;latex-report&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
673&lt;span id="cb7-9"&gt;&lt;a href="#cb7-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;path&lt;/span&gt; = ./latex-report-template&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1012&lt;span id="cb7-9"&gt;&lt;a href="#cb7-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;path&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;./latex-report-template&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
674&lt;span id="cb7-10"&gt;&lt;a href="#cb7-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;description&lt;/span&gt; = &lt;span class="st"&gt;&amp;quot;A latex whitepaper project&amp;quot;&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1013&lt;span id="cb7-10"&gt;&lt;a href="#cb7-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;description&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;A latex whitepaper project&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
675&lt;span id="cb7-11"&gt;&lt;a href="#cb7-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;};&lt;/span&gt;&lt;/span&gt; 1014&lt;span id="cb7-11"&gt;&lt;a href="#cb7-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
676&lt;span id="cb7-12"&gt;&lt;a href="#cb7-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;rust-hello&lt;/span&gt; = {&lt;/span&gt; 1015&lt;span id="cb7-12"&gt;&lt;a href="#cb7-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;rust-hello&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
677&lt;span id="cb7-13"&gt;&lt;a href="#cb7-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;path&lt;/span&gt; = ./rust-hello-template&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1016&lt;span id="cb7-13"&gt;&lt;a href="#cb7-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;path&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;./rust-hello-template&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
678&lt;span id="cb7-14"&gt;&lt;a href="#cb7-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;description&lt;/span&gt; = &lt;span class="st"&gt;&amp;quot;Simple Hello World in Rust&amp;quot;&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1017&lt;span id="cb7-14"&gt;&lt;a href="#cb7-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;description&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;Simple Hello World in Rust&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
679&lt;span id="cb7-15"&gt;&lt;a href="#cb7-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="er"&gt;}&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1018&lt;span id="cb7-15"&gt;&lt;a href="#cb7-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
680&lt;span id="cb7-16"&gt;&lt;a href="#cb7-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="er"&gt;}&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1019&lt;span id="cb7-16"&gt;&lt;a href="#cb7-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
681&lt;span id="cb7-17"&gt;&lt;a href="#cb7-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="er"&gt;}&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1020&lt;span id="cb7-17"&gt;&lt;a href="#cb7-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
682&lt;span id="cb7-18"&gt;&lt;a href="#cb7-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="er"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1021&lt;span id="cb7-18"&gt;&lt;a href="#cb7-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
683&lt;p&gt;The &lt;code&gt;path&lt;/code&gt; attribute to each template is what gets copied over when you initialize a flake. Running &lt;code&gt;nix flake init -t .#latex-report&lt;/code&gt; will initialize the current directory with the contents of &lt;code&gt;./latex-report-template&lt;/code&gt; (we are yet to populate these directories).&lt;/p&gt; 1022&lt;p&gt;The &lt;code&gt;path&lt;/code&gt; attribute to each template is what gets copied
684&lt;p&gt;The output of &lt;code&gt;nix flake show&lt;/code&gt; should be something like:&lt;/p&gt; 1023over when you initialize a flake. Running
685&lt;div class="sourceCode" id="cb8"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb8-1"&gt;&lt;a href="#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake show&lt;/span&gt; 1024&lt;code&gt;nix flake init -t .#latex-report&lt;/code&gt; will initialize the
1025current directory with the contents of
1026&lt;code&gt;./latex-report-template&lt;/code&gt; (we are yet to populate these
1027directories).&lt;/p&gt;
1028&lt;p&gt;The output of &lt;code&gt;nix flake show&lt;/code&gt; should be something
1029like:&lt;/p&gt;
1030&lt;div class="sourceCode" id="cb8"&gt;&lt;pre
1031class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb8-1"&gt;&lt;a href="#cb8-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake show&lt;/span&gt;
686&lt;span id="cb8-2"&gt;&lt;a href="#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;path:/home/np/code/nix-stuff/template-tests?narHash=sha256-{...}&lt;/span&gt;&lt;/span&gt; 1032&lt;span id="cb8-2"&gt;&lt;a href="#cb8-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;path:/home/np/code/nix-stuff/template-tests?narHash=sha256-{...}&lt;/span&gt;&lt;/span&gt;
687&lt;span id="cb8-3"&gt;&lt;a href="#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;└───templates&lt;/span&gt;&lt;/span&gt; 1033&lt;span id="cb8-3"&gt;&lt;a href="#cb8-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;└───templates&lt;/span&gt;&lt;/span&gt;
688&lt;span id="cb8-4"&gt;&lt;a href="#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;├───latex-report:&lt;/span&gt; template: A latex whitepaper project&lt;/span&gt; 1034&lt;span id="cb8-4"&gt;&lt;a href="#cb8-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;├───latex-report:&lt;/span&gt; template: A latex whitepaper project&lt;/span&gt;
689&lt;span id="cb8-5"&gt;&lt;a href="#cb8-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└───rust-hello:&lt;/span&gt; template: Simple Hello World in Rust&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1035&lt;span id="cb8-5"&gt;&lt;a href="#cb8-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└───rust-hello:&lt;/span&gt; template: Simple Hello World in Rust&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
690&lt;p&gt;Populate your template directories with content, here are my template directories for example:&lt;/p&gt; 1036&lt;p&gt;Populate your template directories with content, here are my template
691&lt;div class="sourceCode" id="cb9"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb9-1"&gt;&lt;a href="#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; tree mytemplates&lt;/span&gt; 1037directories for example:&lt;/p&gt;
1038&lt;div class="sourceCode" id="cb9"&gt;&lt;pre
1039class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb9-1"&gt;&lt;a href="#cb9-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; tree mytemplates&lt;/span&gt;
692&lt;span id="cb9-2"&gt;&lt;a href="#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;mytemplates/&lt;/span&gt;&lt;/span&gt; 1040&lt;span id="cb9-2"&gt;&lt;a href="#cb9-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;mytemplates/&lt;/span&gt;&lt;/span&gt;
693&lt;span id="cb9-3"&gt;&lt;a href="#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; flake.nix&lt;/span&gt; 1041&lt;span id="cb9-3"&gt;&lt;a href="#cb9-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; flake.nix&lt;/span&gt;
694&lt;span id="cb9-4"&gt;&lt;a href="#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; latex-report-template&lt;/span&gt; 1042&lt;span id="cb9-4"&gt;&lt;a href="#cb9-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; latex-report-template&lt;/span&gt;
@@ -703,15 +1051,18 @@ H N E I
703&lt;span id="cb9-13"&gt;&lt;a href="#cb9-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└──&lt;/span&gt; src&lt;/span&gt; 1051&lt;span id="cb9-13"&gt;&lt;a href="#cb9-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└──&lt;/span&gt; src&lt;/span&gt;
704&lt;span id="cb9-14"&gt;&lt;a href="#cb9-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└──&lt;/span&gt; main.rs&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1052&lt;span id="cb9-14"&gt;&lt;a href="#cb9-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└──&lt;/span&gt; main.rs&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
705&lt;p&gt;And that’s it! Start using your templates with:&lt;/p&gt; 1053&lt;p&gt;And that’s it! Start using your templates with:&lt;/p&gt;
706&lt;div class="sourceCode" id="cb10"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb10-1"&gt;&lt;a href="#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; ~/mytemplates#rust-hello&lt;/span&gt; 1054&lt;div class="sourceCode" id="cb10"&gt;&lt;pre
1055class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb10-1"&gt;&lt;a href="#cb10-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; ~/mytemplates#rust-hello&lt;/span&gt;
707&lt;span id="cb10-2"&gt;&lt;a href="#cb10-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; tree .&lt;/span&gt; 1056&lt;span id="cb10-2"&gt;&lt;a href="#cb10-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; tree .&lt;/span&gt;
708&lt;span id="cb10-3"&gt;&lt;a href="#cb10-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt;&lt;/span&gt; 1057&lt;span id="cb10-3"&gt;&lt;a href="#cb10-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt;&lt;/span&gt;
709&lt;span id="cb10-4"&gt;&lt;a href="#cb10-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; Cargo.toml&lt;/span&gt; 1058&lt;span id="cb10-4"&gt;&lt;a href="#cb10-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; Cargo.toml&lt;/span&gt;
710&lt;span id="cb10-5"&gt;&lt;a href="#cb10-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; flake.nix&lt;/span&gt; 1059&lt;span id="cb10-5"&gt;&lt;a href="#cb10-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;├──&lt;/span&gt; flake.nix&lt;/span&gt;
711&lt;span id="cb10-6"&gt;&lt;a href="#cb10-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;└──&lt;/span&gt; src&lt;/span&gt; 1060&lt;span id="cb10-6"&gt;&lt;a href="#cb10-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;└──&lt;/span&gt; src&lt;/span&gt;
712&lt;span id="cb10-7"&gt;&lt;a href="#cb10-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└──&lt;/span&gt; main.rs&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1061&lt;span id="cb10-7"&gt;&lt;a href="#cb10-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;└──&lt;/span&gt; main.rs&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
713&lt;p&gt;To avoid writing &lt;code&gt;~/mytemplates&lt;/code&gt; each time, simply alias it to a registry:&lt;/p&gt; 1062&lt;p&gt;To avoid writing &lt;code&gt;~/mytemplates&lt;/code&gt; each time, simply alias
714&lt;div class="sourceCode" id="cb11"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb11-1"&gt;&lt;a href="#cb11-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# alias it to `biscuits`&lt;/span&gt;&lt;/span&gt; 1063it to a registry:&lt;/p&gt;
1064&lt;div class="sourceCode" id="cb11"&gt;&lt;pre
1065class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb11-1"&gt;&lt;a href="#cb11-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# alias it to `biscuits`&lt;/span&gt;&lt;/span&gt;
715&lt;span id="cb11-2"&gt;&lt;a href="#cb11-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix registry add biscuits ~/mytemplates&lt;/span&gt; 1066&lt;span id="cb11-2"&gt;&lt;a href="#cb11-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix registry add biscuits ~/mytemplates&lt;/span&gt;
716&lt;span id="cb11-3"&gt;&lt;a href="#cb11-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 1067&lt;span id="cb11-3"&gt;&lt;a href="#cb11-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
717&lt;span id="cb11-4"&gt;&lt;a href="#cb11-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# you will see it listed under `user` registries&lt;/span&gt;&lt;/span&gt; 1068&lt;span id="cb11-4"&gt;&lt;a href="#cb11-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# you will see it listed under `user` registries&lt;/span&gt;&lt;/span&gt;
@@ -721,59 +1072,102 @@ H N E I
721&lt;span id="cb11-8"&gt;&lt;a href="#cb11-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt; . .&lt;/span&gt; 1072&lt;span id="cb11-8"&gt;&lt;a href="#cb11-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="bu"&gt;.&lt;/span&gt; . .&lt;/span&gt;
722&lt;span id="cb11-9"&gt;&lt;a href="#cb11-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 1073&lt;span id="cb11-9"&gt;&lt;a href="#cb11-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
723&lt;span id="cb11-10"&gt;&lt;a href="#cb11-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; biscuits#latex-report&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1074&lt;span id="cb11-10"&gt;&lt;a href="#cb11-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;$&lt;/span&gt; nix flake init &lt;span class="at"&gt;-t&lt;/span&gt; biscuits#latex-report&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
724&lt;h2 id="extending-the-official-templates"&gt;Extending the official templates&lt;/h2&gt; 1075&lt;h2 id="extending-the-official-templates"&gt;Extending the official
725&lt;p&gt;I personally, would like the &lt;code&gt;biscuits&lt;/code&gt; registry to include not just my homemade templates, but also the templates from &lt;code&gt;NixOS/templates&lt;/code&gt; (and maybe a couple of other repositories in the wild):&lt;/p&gt; 1076templates&lt;/h2&gt;
726&lt;div class="sourceCode" id="cb12"&gt;&lt;pre class="sourceCode nix"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb12-1"&gt;&lt;a href="#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;{&lt;/span&gt;&lt;/span&gt; 1077&lt;p&gt;I personally, would like the &lt;code&gt;biscuits&lt;/code&gt; registry to
727&lt;span id="cb12-2"&gt;&lt;a href="#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;description&lt;/span&gt; = &lt;span class="st"&gt;&amp;quot;Pepper&amp;#39;s flake templates&amp;quot;&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1078include not just my homemade templates, but also the templates from
1079&lt;code&gt;NixOS/templates&lt;/code&gt; (and maybe a couple of other repositories
1080in the wild):&lt;/p&gt;
1081&lt;div class="sourceCode" id="cb12"&gt;&lt;pre
1082class="sourceCode nix"&gt;&lt;code class="sourceCode nix"&gt;&lt;span id="cb12-1"&gt;&lt;a href="#cb12-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
1083&lt;span id="cb12-2"&gt;&lt;a href="#cb12-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;description&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;Pepper&amp;#39;s flake templates&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
728&lt;span id="cb12-3"&gt;&lt;a href="#cb12-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt; 1084&lt;span id="cb12-3"&gt;&lt;a href="#cb12-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt;
729&lt;span id="cb12-4"&gt;&lt;a href="#cb12-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; inputs = {&lt;/span&gt; 1085&lt;span id="cb12-4"&gt;&lt;a href="#cb12-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;+ &lt;span class="va"&gt;inputs&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
730&lt;span id="cb12-5"&gt;&lt;a href="#cb12-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; official-templates.url = github:NixOS/templates&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1086&lt;span id="cb12-5"&gt;&lt;a href="#cb12-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;+ &lt;span class="va"&gt;official-templates&lt;/span&gt;.&lt;span class="va"&gt;url&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="va"&gt;github&lt;/span&gt;&lt;span class="op"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;NixOS/templates&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
731&lt;span id="cb12-6"&gt;&lt;a href="#cb12-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; other-templates.url = github:some-other/templates&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1087&lt;span id="cb12-6"&gt;&lt;a href="#cb12-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;+ &lt;span class="va"&gt;other-templates&lt;/span&gt;.&lt;span class="va"&gt;url&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="va"&gt;github&lt;/span&gt;&lt;span class="op"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;some-other/templates&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
732&lt;span id="cb12-7"&gt;&lt;a href="#cb12-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; }&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1088&lt;span id="cb12-7"&gt;&lt;a href="#cb12-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;+ &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
733&lt;span id="cb12-8"&gt;&lt;a href="#cb12-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt; 1089&lt;span id="cb12-8"&gt;&lt;a href="#cb12-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt;
734&lt;span id="cb12-9"&gt;&lt;a href="#cb12-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;outputs&lt;/span&gt; = { self, official-templates, other-templates ... }: {&lt;/span&gt; 1090&lt;span id="cb12-9"&gt;&lt;a href="#cb12-9" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;outputs&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt; &lt;span class="va"&gt;self&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="va"&gt;official-templates&lt;/span&gt;&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="va"&gt;other-templates&lt;/span&gt; &lt;span class="op"&gt;...&lt;/span&gt; &lt;span class="op"&gt;}&lt;/span&gt;: &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
735&lt;span id="cb12-10"&gt;&lt;a href="#cb12-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt; 1091&lt;span id="cb12-10"&gt;&lt;a href="#cb12-10" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt;
736&lt;span id="cb12-11"&gt;&lt;a href="#cb12-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;templates&lt;/span&gt; = {&lt;/span&gt; 1092&lt;span id="cb12-11"&gt;&lt;a href="#cb12-11" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;templates&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
737&lt;span id="cb12-12"&gt;&lt;a href="#cb12-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;latex-report&lt;/span&gt; = {&lt;/span&gt; 1093&lt;span id="cb12-12"&gt;&lt;a href="#cb12-12" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;latex-report&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
738&lt;span id="cb12-13"&gt;&lt;a href="#cb12-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;path&lt;/span&gt; = ./latex-report-template&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1094&lt;span id="cb12-13"&gt;&lt;a href="#cb12-13" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;path&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;./latex-report-template&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
739&lt;span id="cb12-14"&gt;&lt;a href="#cb12-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;description&lt;/span&gt; = &lt;span class="st"&gt;&amp;quot;A latex whitepaper project&amp;quot;&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1095&lt;span id="cb12-14"&gt;&lt;a href="#cb12-14" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;description&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;A latex whitepaper project&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
740&lt;span id="cb12-15"&gt;&lt;a href="#cb12-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="kw"&gt;};&lt;/span&gt;&lt;/span&gt; 1096&lt;span id="cb12-15"&gt;&lt;a href="#cb12-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
741&lt;span id="cb12-16"&gt;&lt;a href="#cb12-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;rust-hello&lt;/span&gt; = {&lt;/span&gt; 1097&lt;span id="cb12-16"&gt;&lt;a href="#cb12-16" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;rust-hello&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
742&lt;span id="cb12-17"&gt;&lt;a href="#cb12-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;path&lt;/span&gt; = ./rust-hello-template&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1098&lt;span id="cb12-17"&gt;&lt;a href="#cb12-17" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;path&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;./rust-hello-template&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
743&lt;span id="cb12-18"&gt;&lt;a href="#cb12-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="ex"&gt;description&lt;/span&gt; = &lt;span class="st"&gt;&amp;quot;Simple Hello World in Rust, with overloaded Rust toolchain&amp;quot;&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1099&lt;span id="cb12-18"&gt;&lt;a href="#cb12-18" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="va"&gt;description&lt;/span&gt; &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="st"&gt;&amp;quot;Simple Hello World in Rust, with overloaded Rust toolchain&amp;quot;&lt;/span&gt;&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
744&lt;span id="cb12-19"&gt;&lt;a href="#cb12-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="er"&gt;}&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1100&lt;span id="cb12-19"&gt;&lt;a href="#cb12-19" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
745&lt;span id="cb12-20"&gt;&lt;a href="#cb12-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="er"&gt;}&lt;/span&gt;&lt;/span&gt; 1101&lt;span id="cb12-20"&gt;&lt;a href="#cb12-20" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;
746&lt;span id="cb12-21"&gt;&lt;a href="#cb12-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; // official-templates.templates&lt;/span&gt; 1102&lt;span id="cb12-21"&gt;&lt;a href="#cb12-21" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="op"&gt;//&lt;/span&gt; official&lt;span class="op"&gt;-&lt;/span&gt;templates.templates&lt;/span&gt;
747&lt;span id="cb12-22"&gt;&lt;a href="#cb12-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="ex"&gt;+&lt;/span&gt; // other-templates.templates&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1103&lt;span id="cb12-22"&gt;&lt;a href="#cb12-22" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;+&lt;/span&gt; &lt;span class="op"&gt;//&lt;/span&gt; other&lt;span class="op"&gt;-&lt;/span&gt;templates.templates&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
748&lt;span id="cb12-23"&gt;&lt;a href="#cb12-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt; 1104&lt;span id="cb12-23"&gt;&lt;a href="#cb12-23" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;/span&gt;
749&lt;span id="cb12-24"&gt;&lt;a href="#cb12-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="er"&gt;}&lt;/span&gt;&lt;span class="kw"&gt;;&lt;/span&gt;&lt;/span&gt; 1105&lt;span id="cb12-24"&gt;&lt;a href="#cb12-24" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;};&lt;/span&gt;&lt;/span&gt;
750&lt;span id="cb12-25"&gt;&lt;a href="#cb12-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="er"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1106&lt;span id="cb12-25"&gt;&lt;a href="#cb12-25" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
751&lt;p&gt;Running &lt;code&gt;nix flake show biscuits&lt;/code&gt; will now list templates from the &lt;code&gt;biscuits&lt;/code&gt; registry as well as the ones from &lt;code&gt;NixOS/templates&lt;/code&gt;. Ensure that the names don’t collide though.&lt;/p&gt;</description> 1107&lt;p&gt;Running &lt;code&gt;nix flake show biscuits&lt;/code&gt; will now list templates
1108from the &lt;code&gt;biscuits&lt;/code&gt; registry as well as the ones from
1109&lt;code&gt;NixOS/templates&lt;/code&gt;. Ensure that the names don’t collide
1110though.&lt;/p&gt;</description>
752<link>https://peppe.rs/posts/novice_nix:_flake_templates/</link> 1111<link>https://peppe.rs/posts/novice_nix:_flake_templates/</link>
753<pubDate>Tue, 05 Oct 2021 16:49:00 +0000</pubDate> 1112<pubDate>Tue, 05 Oct 2021 16:49:00 +0000</pubDate>
754<guid>https://peppe.rs/posts/novice_nix:_flake_templates/</guid> 1113<guid>https://peppe.rs/posts/novice_nix:_flake_templates/</guid>
755</item> 1114</item>
756<item> 1115<item>
757<title>SDL2 Devlog</title> 1116<title>SDL2 Devlog</title>
758<description>&lt;p&gt;I have been working on an editor for the &lt;a href="https://git.peppe.rs/graphics/obi/about"&gt;One Bit Image&lt;/a&gt; file format in Rust and SDL2. This entry in my blog follows my progress on the editor. The days are listed in reverse chronological order, begin from the bottom, if this is your first time on this page.&lt;/p&gt; 1117<description>&lt;p&gt;I have been working on an editor for the &lt;a
1118href="https://git.peppe.rs/graphics/obi/about"&gt;One Bit Image&lt;/a&gt; file
1119format in Rust and SDL2. This entry in my blog follows my progress on
1120the editor. The days are listed in reverse chronological order, begin
1121from the bottom, if this is your first time on this page.&lt;/p&gt;
759&lt;h3 id="day-20"&gt;Day 20&lt;/h3&gt; 1122&lt;h3 id="day-20"&gt;Day 20&lt;/h3&gt;
760&lt;p&gt;More &lt;code&gt;lisp&lt;/code&gt; stuff! I added a new brush, for rectangular selections. While selection doesn’t do much on its own, the selected area can be passed onto a &lt;code&gt;lisp&lt;/code&gt; procedure, for example, a procedure to draw horizontal black and white lines:&lt;/p&gt; 1123&lt;p&gt;More &lt;code&gt;lisp&lt;/code&gt; stuff! I added a new brush, for rectangular
1124selections. While selection doesn’t do much on its own, the selected
1125area can be passed onto a &lt;code&gt;lisp&lt;/code&gt; procedure, for example, a
1126procedure to draw horizontal black and white lines:&lt;/p&gt;
761&lt;figure&gt; 1127&lt;figure&gt;
762&lt;video src="https://u.peppe.rs/frU.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/frU.mp4"&gt;Day 20&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 20&lt;/figcaption&gt; 1128&lt;video src="https://u.peppe.rs/frU.mp4" controls=""&gt;&lt;a
1129href="https://u.peppe.rs/frU.mp4"&gt;Day 20&lt;/a&gt;&lt;/video&gt;
1130&lt;figcaption aria-hidden="true"&gt;Day 20&lt;/figcaption&gt;
763&lt;/figure&gt; 1131&lt;/figure&gt;
764&lt;h3 id="day-19"&gt;Day 19&lt;/h3&gt; 1132&lt;h3 id="day-19"&gt;Day 19&lt;/h3&gt;
765&lt;p&gt;Attempted &lt;a href="https://peppe.rs/art/conduit.png"&gt;some isometric art&lt;/a&gt; within the editor. The angles displayed alongside the line brush are handly, however, having only a rectangular grid did not help. I implemented an isometric grid today. Isometric grids in pixel art differ in that the tangent of the isometric angle is exactly 0.5! For every pixel down, you go exactly two pixels sideways. The math works out really well in the drawing procedures too, dealing with floating points is a pain.&lt;/p&gt; 1133&lt;p&gt;Attempted &lt;a href="https://peppe.rs/art/conduit.png"&gt;some isometric
1134art&lt;/a&gt; within the editor. The angles displayed alongside the line brush
1135are handly, however, having only a rectangular grid did not help. I
1136implemented an isometric grid today. Isometric grids in pixel art differ
1137in that the tangent of the isometric angle is exactly 0.5! For every
1138pixel down, you go exactly two pixels sideways. The math works out
1139really well in the drawing procedures too, dealing with floating points
1140is a pain.&lt;/p&gt;
766&lt;figure&gt; 1141&lt;figure&gt;
767&lt;img src="https://u.peppe.rs/1Kb.png" alt="Day 19" /&gt;&lt;figcaption aria-hidden="true"&gt;Day 19&lt;/figcaption&gt; 1142&lt;img src="https://u.peppe.rs/1Kb.png" alt="Day 19" /&gt;
1143&lt;figcaption aria-hidden="true"&gt;Day 19&lt;/figcaption&gt;
768&lt;/figure&gt; 1144&lt;/figure&gt;
769&lt;h3 id="day-18"&gt;Day 18&lt;/h3&gt; 1145&lt;h3 id="day-18"&gt;Day 18&lt;/h3&gt;
770&lt;p&gt;I added basic support for guides, they can be added and activated from the &lt;code&gt;lisp&lt;/code&gt; REPL. Another long standing improvement I wanted to make was reworking the pixmap drawing procedure. The old procedure draws a square for each pixel in the pixmap, coloured according to its value in the pixmap. Naturally, this means, for an &lt;strong&gt;NxN&lt;/strong&gt; pixmap, there are &lt;strong&gt;N²&lt;/strong&gt; calls to SDL! I reworked this procedure to compress each line of the pixmap using RLE (run length encoding), and call out to SDL for each run in the line. This drastically improved drawing speeds on larger grids. The following is a comparison between the two procedures, the leftmost picture is the rendered image, the middle picture is the optimized drawing procedure (draws each run instead of pixel), and the right most picture is the primitive drawing procedure (draws each pixel):&lt;/p&gt; 1146&lt;p&gt;I added basic support for guides, they can be added and activated
1147from the &lt;code&gt;lisp&lt;/code&gt; REPL. Another long standing improvement I
1148wanted to make was reworking the pixmap drawing procedure. The old
1149procedure draws a square for each pixel in the pixmap, coloured
1150according to its value in the pixmap. Naturally, this means, for an
1151&lt;strong&gt;NxN&lt;/strong&gt; pixmap, there are &lt;strong&gt;N²&lt;/strong&gt; calls to SDL!
1152I reworked this procedure to compress each line of the pixmap using RLE
1153(run length encoding), and call out to SDL for each run in the line.
1154This drastically improved drawing speeds on larger grids. The following
1155is a comparison between the two procedures, the leftmost picture is the
1156rendered image, the middle picture is the optimized drawing procedure
1157(draws each run instead of pixel), and the right most picture is the
1158primitive drawing procedure (draws each pixel):&lt;/p&gt;
771&lt;figure&gt; 1159&lt;figure&gt;
772&lt;img src="https://u.peppe.rs/U4B.png" alt="Day 18" /&gt;&lt;figcaption aria-hidden="true"&gt;Day 18&lt;/figcaption&gt; 1160&lt;img src="https://u.peppe.rs/U4B.png" alt="Day 18" /&gt;
1161&lt;figcaption aria-hidden="true"&gt;Day 18&lt;/figcaption&gt;
773&lt;/figure&gt; 1162&lt;/figure&gt;
774&lt;h3 id="day-17"&gt;Day 17&lt;/h3&gt; 1163&lt;h3 id="day-17"&gt;Day 17&lt;/h3&gt;
775&lt;p&gt;I decided to give the text-only statusline a touch up, by adding a active color and dither level preview. Aligning the “widget” to the right of statusline involved a lot more than I thought, so I created a ghetto CSS-like rectangle placement system to position containers inside containers:&lt;/p&gt; 1164&lt;p&gt;I decided to give the text-only statusline a touch up, by adding a
776&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// roughly something like this&lt;/span&gt;&lt;/span&gt; 1165active color and dither level preview. Aligning the “widget” to the
1166right of statusline involved a lot more than I thought, so I created a
1167ghetto CSS-like rectangle placement system to position containers inside
1168containers:&lt;/p&gt;
1169&lt;div class="sourceCode" id="cb1"&gt;&lt;pre
1170class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;// roughly something like this&lt;/span&gt;&lt;/span&gt;
777&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; statusline &lt;span class="op"&gt;=&lt;/span&gt; &lt;/span&gt; 1171&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; statusline &lt;span class="op"&gt;=&lt;/span&gt; &lt;/span&gt;
778&lt;span id="cb1-3"&gt;&lt;a href="#cb1-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="pp"&gt;Container::&lt;/span&gt;new(&lt;span class="pp"&gt;Offset::&lt;/span&gt;Left(&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="pp"&gt;Offset::&lt;/span&gt;Bottom(&lt;span class="dv"&gt;40&lt;/span&gt;))&lt;/span&gt; 1172&lt;span id="cb1-3"&gt;&lt;a href="#cb1-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="pp"&gt;Container::&lt;/span&gt;new(&lt;span class="pp"&gt;Offset::&lt;/span&gt;Left(&lt;span class="dv"&gt;0&lt;/span&gt;)&lt;span class="op"&gt;,&lt;/span&gt; &lt;span class="pp"&gt;Offset::&lt;/span&gt;Bottom(&lt;span class="dv"&gt;40&lt;/span&gt;))&lt;/span&gt;
779&lt;span id="cb1-4"&gt;&lt;a href="#cb1-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;width(&lt;span class="pp"&gt;Size::&lt;/span&gt;Max)&lt;/span&gt; 1173&lt;span id="cb1-4"&gt;&lt;a href="#cb1-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="op"&gt;.&lt;/span&gt;width(&lt;span class="pp"&gt;Size::&lt;/span&gt;Max)&lt;/span&gt;
@@ -790,14 +1184,20 @@ H N E I
790&lt;span id="cb1-15"&gt;&lt;a href="#cb1-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1184&lt;span id="cb1-15"&gt;&lt;a href="#cb1-15" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
791&lt;p&gt;The result (brush preview on the bottom right):&lt;/p&gt; 1185&lt;p&gt;The result (brush preview on the bottom right):&lt;/p&gt;
792&lt;figure&gt; 1186&lt;figure&gt;
793&lt;video src="https://u.peppe.rs/OtU.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/OtU.mp4"&gt;Day 17&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 17&lt;/figcaption&gt; 1187&lt;video src="https://u.peppe.rs/OtU.mp4" controls=""&gt;&lt;a
1188href="https://u.peppe.rs/OtU.mp4"&gt;Day 17&lt;/a&gt;&lt;/video&gt;
1189&lt;figcaption aria-hidden="true"&gt;Day 17&lt;/figcaption&gt;
794&lt;/figure&gt; 1190&lt;/figure&gt;
795&lt;h3 id="day-16"&gt;Day 16&lt;/h3&gt; 1191&lt;h3 id="day-16"&gt;Day 16&lt;/h3&gt;
796&lt;p&gt;The embedded lisp is coming along nicely, users can load a custom &lt;code&gt;rc.lisp&lt;/code&gt;, which is evaluated on startup. To disable to grid on start, for example:&lt;/p&gt; 1192&lt;p&gt;The embedded lisp is coming along nicely, users can load a custom
797&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;;;; rc.lisp&lt;/span&gt;&lt;/span&gt; 1193&lt;code&gt;rc.lisp&lt;/code&gt;, which is evaluated on startup. To disable to grid
1194on start, for example:&lt;/p&gt;
1195&lt;div class="sourceCode" id="cb2"&gt;&lt;pre
1196class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;;;; rc.lisp&lt;/span&gt;&lt;/span&gt;
798&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(toggle-grid)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1197&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(toggle-grid)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
799&lt;p&gt;Some aliases to switch between brushes:&lt;/p&gt; 1198&lt;p&gt;Some aliases to switch between brushes:&lt;/p&gt;
800&lt;div class="sourceCode" id="cb3"&gt;&lt;pre class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;;;; rc.lisp&lt;/span&gt;&lt;/span&gt; 1199&lt;div class="sourceCode" id="cb3"&gt;&lt;pre
1200class="sourceCode scheme"&gt;&lt;code class="sourceCode scheme"&gt;&lt;span id="cb3-1"&gt;&lt;a href="#cb3-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;;;; rc.lisp&lt;/span&gt;&lt;/span&gt;
801&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(&lt;span class="ex"&gt;define&lt;/span&gt;&lt;span class="fu"&gt; &lt;/span&gt;(brush kind)&lt;/span&gt; 1201&lt;span id="cb3-2"&gt;&lt;a href="#cb3-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(&lt;span class="ex"&gt;define&lt;/span&gt;&lt;span class="fu"&gt; &lt;/span&gt;(brush kind)&lt;/span&gt;
802&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="kw"&gt;cond&lt;/span&gt;&lt;/span&gt; 1202&lt;span id="cb3-3"&gt;&lt;a href="#cb3-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="kw"&gt;cond&lt;/span&gt;&lt;/span&gt;
803&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ((&lt;span class="kw"&gt;eq?&lt;/span&gt; kind &amp;#39;f) (brush-fill))&lt;/span&gt; 1203&lt;span id="cb3-4"&gt;&lt;a href="#cb3-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ((&lt;span class="kw"&gt;eq?&lt;/span&gt; kind &amp;#39;f) (brush-fill))&lt;/span&gt;
@@ -805,90 +1205,195 @@ H N E I
805&lt;span id="cb3-6"&gt;&lt;a href="#cb3-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ((&lt;span class="kw"&gt;eq?&lt;/span&gt; kind &amp;#39;l) (brush-line))&lt;/span&gt; 1205&lt;span id="cb3-6"&gt;&lt;a href="#cb3-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ((&lt;span class="kw"&gt;eq?&lt;/span&gt; kind &amp;#39;l) (brush-line))&lt;/span&gt;
806&lt;span id="cb3-7"&gt;&lt;a href="#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ((&lt;span class="kw"&gt;eq?&lt;/span&gt; kind &amp;#39;l+) (brush-line-extend))&lt;/span&gt; 1206&lt;span id="cb3-7"&gt;&lt;a href="#cb3-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ((&lt;span class="kw"&gt;eq?&lt;/span&gt; kind &amp;#39;l+) (brush-line-extend))&lt;/span&gt;
807&lt;span id="cb3-8"&gt;&lt;a href="#cb3-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="kw"&gt;else&lt;/span&gt; (brush-circle))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1207&lt;span id="cb3-8"&gt;&lt;a href="#cb3-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="kw"&gt;else&lt;/span&gt; (brush-circle))))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
808&lt;p&gt;The following script draws a straight line along a given axis, at a given distance from the canvas boundary:&lt;/p&gt; 1208&lt;p&gt;The following script draws a straight line along a given axis, at a
1209given distance from the canvas boundary:&lt;/p&gt;
809&lt;figure&gt; 1210&lt;figure&gt;
810&lt;video src="https://u.peppe.rs/b3i.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/b3i.mp4"&gt;Day 16&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 16&lt;/figcaption&gt; 1211&lt;video src="https://u.peppe.rs/b3i.mp4" controls=""&gt;&lt;a
1212href="https://u.peppe.rs/b3i.mp4"&gt;Day 16&lt;/a&gt;&lt;/video&gt;
1213&lt;figcaption aria-hidden="true"&gt;Day 16&lt;/figcaption&gt;
811&lt;/figure&gt; 1214&lt;/figure&gt;
812&lt;h3 id="day-15"&gt;Day 15&lt;/h3&gt; 1215&lt;h3 id="day-15"&gt;Day 15&lt;/h3&gt;
813&lt;p&gt;I began writing a standard library for the lisp, in lisp. It includes basic list operations: &lt;code&gt;car&lt;/code&gt;, &lt;code&gt;cdr&lt;/code&gt;, &lt;code&gt;null?&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, higher order functions: &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;fold&lt;/code&gt;:&lt;/p&gt; 1216&lt;p&gt;I began writing a standard library for the lisp, in lisp. It includes
814&lt;div class="sourceCode" id="cb4"&gt;&lt;pre class="sourceCode lisp"&gt;&lt;code class="sourceCode commonlisp"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(define (member? item ls)&lt;/span&gt; 1217basic list operations: &lt;code&gt;car&lt;/code&gt;, &lt;code&gt;cdr&lt;/code&gt;,
1218&lt;code&gt;null?&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, higher order functions:
1219&lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;fold&lt;/code&gt;:&lt;/p&gt;
1220&lt;div class="sourceCode" id="cb4"&gt;&lt;pre
1221class="sourceCode lisp"&gt;&lt;code class="sourceCode commonlisp"&gt;&lt;span id="cb4-1"&gt;&lt;a href="#cb4-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;(define (member? item ls)&lt;/span&gt;
815&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (fold &lt;span class="dv"&gt;#f&lt;/span&gt;&lt;/span&gt; 1222&lt;span id="cb4-2"&gt;&lt;a href="#cb4-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (fold &lt;span class="dv"&gt;#f&lt;/span&gt;&lt;/span&gt;
816&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="kw"&gt;lambda&lt;/span&gt; (acc x) (&lt;span class="kw"&gt;or&lt;/span&gt; acc (eq? item x)))&lt;/span&gt; 1223&lt;span id="cb4-3"&gt;&lt;a href="#cb4-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; (&lt;span class="kw"&gt;lambda&lt;/span&gt; (acc x) (&lt;span class="kw"&gt;or&lt;/span&gt; acc (eq? item x)))&lt;/span&gt;
817&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ls))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1224&lt;span id="cb4-4"&gt;&lt;a href="#cb4-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; ls))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
818&lt;h3 id="day-14"&gt;Day 14&lt;/h3&gt; 1225&lt;h3 id="day-14"&gt;Day 14&lt;/h3&gt;
819&lt;p&gt;I attempted a &lt;a href="https://peppe.rs/art/ramen_noodles.png"&gt;small art piece&lt;/a&gt; using the editor, while it was largely usable, I felt a certain lack of feedback. The brushes just didn’t relay as much info as I’d have liked, for example, the approximate points of the line or the angle made by the line against the x-axis. Unfortunately, the existing infrastructure around brushes and line drawing didn’t easily allow for this either. I went ahead and reimplemented brushes, and added a new flood fill brush too:&lt;/p&gt; 1226&lt;p&gt;I attempted a &lt;a href="https://peppe.rs/art/ramen_noodles.png"&gt;small
1227art piece&lt;/a&gt; using the editor, while it was largely usable, I felt a
1228certain lack of feedback. The brushes just didn’t relay as much info as
1229I’d have liked, for example, the approximate points of the line or the
1230angle made by the line against the x-axis. Unfortunately, the existing
1231infrastructure around brushes and line drawing didn’t easily allow for
1232this either. I went ahead and reimplemented brushes, and added a new
1233flood fill brush too:&lt;/p&gt;
820&lt;figure&gt; 1234&lt;figure&gt;
821&lt;video src="https://u.peppe.rs/8q.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/8q.mp4"&gt;Day 14&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 14&lt;/figcaption&gt; 1235&lt;video src="https://u.peppe.rs/8q.mp4" controls=""&gt;&lt;a
1236href="https://u.peppe.rs/8q.mp4"&gt;Day 14&lt;/a&gt;&lt;/video&gt;
1237&lt;figcaption aria-hidden="true"&gt;Day 14&lt;/figcaption&gt;
822&lt;/figure&gt; 1238&lt;/figure&gt;
823&lt;h3 id="day-13"&gt;Day 13&lt;/h3&gt; 1239&lt;h3 id="day-13"&gt;Day 13&lt;/h3&gt;
824&lt;p&gt;I added a few more forms to the &lt;code&gt;lisp&lt;/code&gt; evaluator. It handles recursion, definitions, variable mutation and more. The prelude contains 20 subroutines so far, including comparision and logic operators. The REPL interface on the SDL side requires some UX tweaks; environment based completion, readline motions sound doable.&lt;/p&gt; 1240&lt;p&gt;I added a few more forms to the &lt;code&gt;lisp&lt;/code&gt; evaluator. It
1241handles recursion, definitions, variable mutation and more. The prelude
1242contains 20 subroutines so far, including comparision and logic
1243operators. The REPL interface on the SDL side requires some UX tweaks;
1244environment based completion, readline motions sound doable.&lt;/p&gt;
825&lt;figure&gt; 1245&lt;figure&gt;
826&lt;video src="https://u.peppe.rs/u3.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/u3.mp4"&gt;Day 13&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 13&lt;/figcaption&gt; 1246&lt;video src="https://u.peppe.rs/u3.mp4" controls=""&gt;&lt;a
1247href="https://u.peppe.rs/u3.mp4"&gt;Day 13&lt;/a&gt;&lt;/video&gt;
1248&lt;figcaption aria-hidden="true"&gt;Day 13&lt;/figcaption&gt;
827&lt;/figure&gt; 1249&lt;/figure&gt;
828&lt;h3 id="day-12"&gt;Day 12&lt;/h3&gt; 1250&lt;h3 id="day-12"&gt;Day 12&lt;/h3&gt;
829&lt;p&gt;I lifted most of &lt;a href="https://github.com/murarth/ketos"&gt;murarth/ketos&lt;/a&gt; into the editor. &lt;code&gt;ketos&lt;/code&gt;’s implementation of &lt;code&gt;lisp&lt;/code&gt; is too vast for my use case. For example, the editor does not need data types to handle raw strings or byte strings. I have got a basic evaluator running inside the SDL2 context (notice the &lt;code&gt;lisp&lt;/code&gt; REPL at the bottom of the window). Over the following days, I intend to create a set of prelude functions to manipulate the pixmap. Users can implement their own brushes, dithering patterns, keybinds and more (hopefully).&lt;/p&gt; 1251&lt;p&gt;I lifted most of &lt;a
1252href="https://github.com/murarth/ketos"&gt;murarth/ketos&lt;/a&gt; into the
1253editor. &lt;code&gt;ketos&lt;/code&gt;’s implementation of &lt;code&gt;lisp&lt;/code&gt; is too
1254vast for my use case. For example, the editor does not need data types
1255to handle raw strings or byte strings. I have got a basic evaluator
1256running inside the SDL2 context (notice the &lt;code&gt;lisp&lt;/code&gt; REPL at
1257the bottom of the window). Over the following days, I intend to create a
1258set of prelude functions to manipulate the pixmap. Users can implement
1259their own brushes, dithering patterns, keybinds and more
1260(hopefully).&lt;/p&gt;
830&lt;figure&gt; 1261&lt;figure&gt;
831&lt;video src="https://u.peppe.rs/y0.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/y0.mp4"&gt;Day 12&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 12&lt;/figcaption&gt; 1262&lt;video src="https://u.peppe.rs/y0.mp4" controls=""&gt;&lt;a
1263href="https://u.peppe.rs/y0.mp4"&gt;Day 12&lt;/a&gt;&lt;/video&gt;
1264&lt;figcaption aria-hidden="true"&gt;Day 12&lt;/figcaption&gt;
832&lt;/figure&gt; 1265&lt;/figure&gt;
833&lt;h3 id="day-11"&gt;Day 11&lt;/h3&gt; 1266&lt;h3 id="day-11"&gt;Day 11&lt;/h3&gt;
834&lt;p&gt;I intend to supplement the editor with scripting language and an inbuilt REPL for the same. I began by implementing a text box widget from scratch, with history and readline like editing:&lt;/p&gt; 1267&lt;p&gt;I intend to supplement the editor with scripting language and an
1268inbuilt REPL for the same. I began by implementing a text box widget
1269from scratch, with history and readline like editing:&lt;/p&gt;
835&lt;figure&gt; 1270&lt;figure&gt;
836&lt;video src="https://u.peppe.rs/Mh.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/Mh.mp4"&gt;Day 11&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 11&lt;/figcaption&gt; 1271&lt;video src="https://u.peppe.rs/Mh.mp4" controls=""&gt;&lt;a
1272href="https://u.peppe.rs/Mh.mp4"&gt;Day 11&lt;/a&gt;&lt;/video&gt;
1273&lt;figcaption aria-hidden="true"&gt;Day 11&lt;/figcaption&gt;
837&lt;/figure&gt; 1274&lt;/figure&gt;
838&lt;h3 id="day-10"&gt;Day 10&lt;/h3&gt; 1275&lt;h3 id="day-10"&gt;Day 10&lt;/h3&gt;
839&lt;p&gt;I started reading up on dithering methods and half-toning, I wanted to create a dithering brush that would automatically produce popular dithering patterns. The method that caught my eye (and also the one used most often in pixel art), was Bayer’s ordered dithering. When applied to a black and white image, each pixel, based on its intensity, is mapped to a 4x4 grid of pixels. A completely empty (completely black) 4x4 grid represents zero intensity, and a filled 4x4 grid represents full intensity. Bayer’s ordered dithering can produce 15 steps of intensity between zero and full (by switching on exactly 1 pixel more at each level), thus, being able to draw 17 “shades” from white to black. Creating a dithering brush from here was fairly trivial. Our pixmap is supposed to represent the final dithered image, it must be divided into 4x4 grids. Each grid is colored based on the intensity of the brush passing over it:&lt;/p&gt; 1276&lt;p&gt;I started reading up on dithering methods and half-toning, I wanted
1277to create a dithering brush that would automatically produce popular
1278dithering patterns. The method that caught my eye (and also the one used
1279most often in pixel art), was Bayer’s ordered dithering. When applied to
1280a black and white image, each pixel, based on its intensity, is mapped
1281to a 4x4 grid of pixels. A completely empty (completely black) 4x4 grid
1282represents zero intensity, and a filled 4x4 grid represents full
1283intensity. Bayer’s ordered dithering can produce 15 steps of intensity
1284between zero and full (by switching on exactly 1 pixel more at each
1285level), thus, being able to draw 17 “shades” from white to black.
1286Creating a dithering brush from here was fairly trivial. Our pixmap is
1287supposed to represent the final dithered image, it must be divided into
12884x4 grids. Each grid is colored based on the intensity of the brush
1289passing over it:&lt;/p&gt;
840&lt;figure&gt; 1290&lt;figure&gt;
841&lt;img src="https://u.peppe.rs/Mn.png" alt="Day 10" /&gt;&lt;figcaption aria-hidden="true"&gt;Day 10&lt;/figcaption&gt; 1291&lt;img src="https://u.peppe.rs/Mn.png" alt="Day 10" /&gt;
1292&lt;figcaption aria-hidden="true"&gt;Day 10&lt;/figcaption&gt;
842&lt;/figure&gt; 1293&lt;/figure&gt;
843&lt;h3 id="day-9"&gt;Day 9&lt;/h3&gt; 1294&lt;h3 id="day-9"&gt;Day 9&lt;/h3&gt;
844&lt;p&gt;I started working towards an interface. I like the idea of a largely read-only HUD, i. e., an interface that simply describes the state of the application. Changes to this state are initiated via keybinds or text commands. I am proud of the symmetry indicator; &lt;code&gt;-&lt;/code&gt; for horizontal symmetry, &lt;code&gt;|&lt;/code&gt; for vertical symmetry, &lt;code&gt;+&lt;/code&gt; for radial symmetry.&lt;/p&gt; 1295&lt;p&gt;I started working towards an interface. I like the idea of a largely
1296read-only HUD, i. e., an interface that simply describes the state of
1297the application. Changes to this state are initiated via keybinds or
1298text commands. I am proud of the symmetry indicator; &lt;code&gt;-&lt;/code&gt; for
1299horizontal symmetry, &lt;code&gt;|&lt;/code&gt; for vertical symmetry,
1300&lt;code&gt;+&lt;/code&gt; for radial symmetry.&lt;/p&gt;
845&lt;figure&gt; 1301&lt;figure&gt;
846&lt;img src="https://u.peppe.rs/hx.png" alt="Day 9" /&gt;&lt;figcaption aria-hidden="true"&gt;Day 9&lt;/figcaption&gt; 1302&lt;img src="https://u.peppe.rs/hx.png" alt="Day 9" /&gt;
1303&lt;figcaption aria-hidden="true"&gt;Day 9&lt;/figcaption&gt;
847&lt;/figure&gt; 1304&lt;/figure&gt;
848&lt;h3 id="day-8"&gt;Day 8&lt;/h3&gt; 1305&lt;h3 id="day-8"&gt;Day 8&lt;/h3&gt;
849&lt;p&gt;One of my favourite features of GIMP was symmetric editing. I added some coordinate geometry primitives to my pixmap abstraction, allowing for mirroring and reflecting figures about lines or points. The result was an ergonomic function that applies symmetry to any painting operation, (undo/redo works as expected):&lt;/p&gt; 1306&lt;p&gt;One of my favourite features of GIMP was symmetric editing. I added
850&lt;div class="sourceCode" id="cb5"&gt;&lt;pre class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb5-1"&gt;&lt;a href="#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; line &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;self&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;pixmap&lt;span class="op"&gt;.&lt;/span&gt;get_line(start&lt;span class="op"&gt;,&lt;/span&gt; end)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 1307some coordinate geometry primitives to my pixmap abstraction, allowing
1308for mirroring and reflecting figures about lines or points. The result
1309was an ergonomic function that applies symmetry to any painting
1310operation, (undo/redo works as expected):&lt;/p&gt;
1311&lt;div class="sourceCode" id="cb5"&gt;&lt;pre
1312class="sourceCode rust"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb5-1"&gt;&lt;a href="#cb5-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; line &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;self&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;pixmap&lt;span class="op"&gt;.&lt;/span&gt;get_line(start&lt;span class="op"&gt;,&lt;/span&gt; end)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
851&lt;span id="cb5-2"&gt;&lt;a href="#cb5-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; sym_line &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;self&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;symmetry&lt;span class="op"&gt;.&lt;/span&gt;apply(&lt;span class="op"&gt;&amp;amp;&lt;/span&gt;line)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt; 1313&lt;span id="cb5-2"&gt;&lt;a href="#cb5-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;let&lt;/span&gt; sym_line &lt;span class="op"&gt;=&lt;/span&gt; &lt;span class="kw"&gt;self&lt;/span&gt;&lt;span class="op"&gt;.&lt;/span&gt;symmetry&lt;span class="op"&gt;.&lt;/span&gt;apply(&lt;span class="op"&gt;&amp;amp;&lt;/span&gt;line)&lt;span class="op"&gt;;&lt;/span&gt;&lt;/span&gt;
852&lt;span id="cb5-3"&gt;&lt;a href="#cb5-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="kw"&gt;for&lt;/span&gt; point on line&lt;span class="op"&gt;.&lt;/span&gt;extend(sym_line) &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt; 1314&lt;span id="cb5-3"&gt;&lt;a href="#cb5-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="cf"&gt;for&lt;/span&gt; point on line&lt;span class="op"&gt;.&lt;/span&gt;extend(sym_line) &lt;span class="op"&gt;{&lt;/span&gt;&lt;/span&gt;
853&lt;span id="cb5-4"&gt;&lt;a href="#cb5-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// draw to window&lt;/span&gt;&lt;/span&gt; 1315&lt;span id="cb5-4"&gt;&lt;a href="#cb5-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt; &lt;span class="co"&gt;// draw to window&lt;/span&gt;&lt;/span&gt;
854&lt;span id="cb5-5"&gt;&lt;a href="#cb5-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1316&lt;span id="cb5-5"&gt;&lt;a href="#cb5-5" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="op"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
855&lt;figure&gt; 1317&lt;figure&gt;
856&lt;video src="https://u.peppe.rs/B1.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/B1.mp4"&gt;Day 8&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 8&lt;/figcaption&gt; 1318&lt;video src="https://u.peppe.rs/B1.mp4" controls=""&gt;&lt;a
1319href="https://u.peppe.rs/B1.mp4"&gt;Day 8&lt;/a&gt;&lt;/video&gt;
1320&lt;figcaption aria-hidden="true"&gt;Day 8&lt;/figcaption&gt;
857&lt;/figure&gt; 1321&lt;/figure&gt;
858&lt;h3 id="day-7"&gt;Day 7&lt;/h3&gt; 1322&lt;h3 id="day-7"&gt;Day 7&lt;/h3&gt;
859&lt;p&gt;Bresenham saves the day again! This time, I implemented his line drawing algorithm, to, well, draw lines. Each point on the line is then “buffed” based on the active brush size. Today’s changes fit in very well with the undo system and the brush size feature. Creating the right abstractions, one at a time :)&lt;/p&gt; 1323&lt;p&gt;Bresenham saves the day again! This time, I implemented his line
1324drawing algorithm, to, well, draw lines. Each point on the line is then
1325“buffed” based on the active brush size. Today’s changes fit in very
1326well with the undo system and the brush size feature. Creating the right
1327abstractions, one at a time :)&lt;/p&gt;
860&lt;figure&gt; 1328&lt;figure&gt;
861&lt;video src="https://u.peppe.rs/xt.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/xt.mp4"&gt;Day 7&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 7&lt;/figcaption&gt; 1329&lt;video src="https://u.peppe.rs/xt.mp4" controls=""&gt;&lt;a
1330href="https://u.peppe.rs/xt.mp4"&gt;Day 7&lt;/a&gt;&lt;/video&gt;
1331&lt;figcaption aria-hidden="true"&gt;Day 7&lt;/figcaption&gt;
862&lt;/figure&gt; 1332&lt;/figure&gt;
863&lt;h3 id="day-6"&gt;Day 6&lt;/h3&gt; 1333&lt;h3 id="day-6"&gt;Day 6&lt;/h3&gt;
864&lt;p&gt;I extended Bresenham’s algorithm to draw not just circle outlines, but also generate their fills. Unlike Bresenham’s algorithm, this variant generates points for two quadrants at once, these points are mirrored over the dividing axis to generate the other two quadrants.&lt;/p&gt; 1334&lt;p&gt;I extended Bresenham’s algorithm to draw not just circle outlines,
1335but also generate their fills. Unlike Bresenham’s algorithm, this
1336variant generates points for two quadrants at once, these points are
1337mirrored over the dividing axis to generate the other two quadrants.&lt;/p&gt;
865&lt;figure&gt; 1338&lt;figure&gt;
866&lt;img src="https://u.peppe.rs/f3.png" alt="Day 6" /&gt;&lt;figcaption aria-hidden="true"&gt;Day 6&lt;/figcaption&gt; 1339&lt;img src="https://u.peppe.rs/f3.png" alt="Day 6" /&gt;
1340&lt;figcaption aria-hidden="true"&gt;Day 6&lt;/figcaption&gt;
867&lt;/figure&gt; 1341&lt;/figure&gt;
868&lt;h3 id="day-5"&gt;Day 5&lt;/h3&gt; 1342&lt;h3 id="day-5"&gt;Day 5&lt;/h3&gt;
869&lt;p&gt;I discovered and implemented Bresenham’s algorithm for efficient circle drawing. The algorithm allowed for sized circular brushes, something I really liked from GIMP. Very convenient that the Wikipedia page for Bresenham’s algorithm also includes a section about optimizing for integer based arithmetic. I managed to abstract out another giant component of the application, the pixmap. Any image is just a grid of pixels (a pixmap), where the pixel’s value is decided by the application (1-bit in my case). I could potentially extend the application to a 24-bit image editor!&lt;/p&gt; 1343&lt;p&gt;I discovered and implemented Bresenham’s algorithm for efficient
1344circle drawing. The algorithm allowed for sized circular brushes,
1345something I really liked from GIMP. Very convenient that the Wikipedia
1346page for Bresenham’s algorithm also includes a section about optimizing
1347for integer based arithmetic. I managed to abstract out another giant
1348component of the application, the pixmap. Any image is just a grid of
1349pixels (a pixmap), where the pixel’s value is decided by the application
1350(1-bit in my case). I could potentially extend the application to a
135124-bit image editor!&lt;/p&gt;
870&lt;figure&gt; 1352&lt;figure&gt;
871&lt;video src="https://u.peppe.rs/Kh.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/Kh.mp4"&gt;Day 5&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 5&lt;/figcaption&gt; 1353&lt;video src="https://u.peppe.rs/Kh.mp4" controls=""&gt;&lt;a
1354href="https://u.peppe.rs/Kh.mp4"&gt;Day 5&lt;/a&gt;&lt;/video&gt;
1355&lt;figcaption aria-hidden="true"&gt;Day 5&lt;/figcaption&gt;
872&lt;/figure&gt; 1356&lt;/figure&gt;
873&lt;h3 id="day-4"&gt;Day 4&lt;/h3&gt; 1357&lt;h3 id="day-4"&gt;Day 4&lt;/h3&gt;
874&lt;p&gt;I created a generic “undo stack” data structure that allows for infinite “undos” and “redos”. Every modification operation to the grid is persisted to the application state. A couple of keybinds allow the user to revert and re-apply these operations! I expect abstracting this component will come in handy down the line.&lt;/p&gt; 1358&lt;p&gt;I created a generic “undo stack” data structure that allows for
1359infinite “undos” and “redos”. Every modification operation to the grid
1360is persisted to the application state. A couple of keybinds allow the
1361user to revert and re-apply these operations! I expect abstracting this
1362component will come in handy down the line.&lt;/p&gt;
875&lt;figure&gt; 1363&lt;figure&gt;
876&lt;video src="https://u.peppe.rs/w5.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/w5.mp4"&gt;Day 4&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 4&lt;/figcaption&gt; 1364&lt;video src="https://u.peppe.rs/w5.mp4" controls=""&gt;&lt;a
1365href="https://u.peppe.rs/w5.mp4"&gt;Day 4&lt;/a&gt;&lt;/video&gt;
1366&lt;figcaption aria-hidden="true"&gt;Day 4&lt;/figcaption&gt;
877&lt;/figure&gt; 1367&lt;/figure&gt;
878&lt;h3 id="day-3"&gt;Day 3&lt;/h3&gt; 1368&lt;h3 id="day-3"&gt;Day 3&lt;/h3&gt;
879&lt;p&gt;I implemented the bare minimum required to call the program an “editor”. The application displays a grid, tracks mouse events, paints white to the canvas on left click, and black to the canvas on right click. I created a make-shift MVC architecture à la Elm in Rust.&lt;/p&gt; 1369&lt;p&gt;I implemented the bare minimum required to call the program an
1370“editor”. The application displays a grid, tracks mouse events, paints
1371white to the canvas on left click, and black to the canvas on right
1372click. I created a make-shift MVC architecture à la Elm in Rust.&lt;/p&gt;
880&lt;figure&gt; 1373&lt;figure&gt;
881&lt;video src="https://u.peppe.rs/GF.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/GF.mp4"&gt;Day 3&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 3&lt;/figcaption&gt; 1374&lt;video src="https://u.peppe.rs/GF.mp4" controls=""&gt;&lt;a
1375href="https://u.peppe.rs/GF.mp4"&gt;Day 3&lt;/a&gt;&lt;/video&gt;
1376&lt;figcaption aria-hidden="true"&gt;Day 3&lt;/figcaption&gt;
882&lt;/figure&gt; 1377&lt;/figure&gt;
883&lt;h3 id="day-2"&gt;Day 2&lt;/h3&gt; 1378&lt;h3 id="day-2"&gt;Day 2&lt;/h3&gt;
884&lt;p&gt;I started figuring out event handling today. Implemented a couple of keybinds to zoom in/out of the drawing area. Conversions of SDL2 coordinates (measured in signed 32 bit integers) to my internal “drawing area” coordinates (measured in unsigned 32 bit integers) is very annoying. Hopefully the unchecked conversions won’t haunt me later.&lt;/p&gt; 1379&lt;p&gt;I started figuring out event handling today. Implemented a couple of
1380keybinds to zoom in/out of the drawing area. Conversions of SDL2
1381coordinates (measured in signed 32 bit integers) to my internal “drawing
1382area” coordinates (measured in unsigned 32 bit integers) is very
1383annoying. Hopefully the unchecked conversions won’t haunt me later.&lt;/p&gt;
885&lt;figure&gt; 1384&lt;figure&gt;
886&lt;video src="https://u.peppe.rs/L4.mp4" controls=""&gt;&lt;a href="https://u.peppe.rs/L4.mp4"&gt;Day 2&lt;/a&gt;&lt;/video&gt;&lt;figcaption aria-hidden="true"&gt;Day 2&lt;/figcaption&gt; 1385&lt;video src="https://u.peppe.rs/L4.mp4" controls=""&gt;&lt;a
1386href="https://u.peppe.rs/L4.mp4"&gt;Day 2&lt;/a&gt;&lt;/video&gt;
1387&lt;figcaption aria-hidden="true"&gt;Day 2&lt;/figcaption&gt;
887&lt;/figure&gt; 1388&lt;/figure&gt;
888&lt;h3 id="day-1"&gt;Day 1&lt;/h3&gt; 1389&lt;h3 id="day-1"&gt;Day 1&lt;/h3&gt;
889&lt;p&gt;Getting started with Rust and SDL2 is very straightforward. The &lt;code&gt;rust-sdl2&lt;/code&gt; library contains some detailed examples that allowed me to get all the way to drawing a grid from a &lt;code&gt;Vec&amp;lt;bool&amp;gt;&lt;/code&gt;:&lt;/p&gt; 1390&lt;p&gt;Getting started with Rust and SDL2 is very straightforward. The
1391&lt;code&gt;rust-sdl2&lt;/code&gt; library contains some detailed examples that
1392allowed me to get all the way to drawing a grid from a
1393&lt;code&gt;Vec&amp;lt;bool&amp;gt;&lt;/code&gt;:&lt;/p&gt;
890&lt;figure&gt; 1394&lt;figure&gt;
891&lt;img src="https://u.peppe.rs/Ma.png" alt="Day 1" /&gt;&lt;figcaption aria-hidden="true"&gt;Day 1&lt;/figcaption&gt; 1395&lt;img src="https://u.peppe.rs/Ma.png" alt="Day 1" /&gt;
1396&lt;figcaption aria-hidden="true"&gt;Day 1&lt;/figcaption&gt;
892&lt;/figure&gt;</description> 1397&lt;/figure&gt;</description>
893<link>https://peppe.rs/posts/SDL2_devlog/</link> 1398<link>https://peppe.rs/posts/SDL2_devlog/</link>
894<pubDate>Sun, 11 Apr 2021 10:46:00 +0000</pubDate> 1399<pubDate>Sun, 11 Apr 2021 10:46:00 +0000</pubDate>
@@ -896,11 +1401,21 @@ H N E I
896</item> 1401</item>
897<item> 1402<item>
898<title>Self-hosting Git</title> 1403<title>Self-hosting Git</title>
899<description>&lt;p&gt;Earlier this week, I began migrating my repositories from Github to &lt;a href="https://git.zx2c4.com/cgit/about/"&gt;cgit&lt;/a&gt;. If you care at all about big corporates turning open-source into a T-shirt farming service, this is the way to go.&lt;/p&gt; 1404<description>&lt;p&gt;Earlier this week, I began migrating my repositories from Github to
1405&lt;a href="https://git.zx2c4.com/cgit/about/"&gt;cgit&lt;/a&gt;. If you care at all
1406about big corporates turning open-source into a T-shirt farming service,
1407this is the way to go.&lt;/p&gt;
900&lt;h3 id="offerings"&gt;Offerings&lt;/h3&gt; 1408&lt;h3 id="offerings"&gt;Offerings&lt;/h3&gt;
901&lt;p&gt;cgit is &lt;em&gt;very&lt;/em&gt; bare bones. It is &lt;a href="https://tools.ietf.org/html/rfc3875"&gt;cgi-based&lt;/a&gt; web interface to git, and nothing more. You may browse repositories, view diffs, commit logs and even clone via http. If you are looking to replace Github with cgit, keep in mind that cgit does not handle issues or pull/merge requests. If people wish to contribute to your work, they would have to send you a patch via email.&lt;/p&gt; 1409&lt;p&gt;cgit is &lt;em&gt;very&lt;/em&gt; bare bones. It is &lt;a
1410href="https://tools.ietf.org/html/rfc3875"&gt;cgi-based&lt;/a&gt; web interface
1411to git, and nothing more. You may browse repositories, view diffs,
1412commit logs and even clone via http. If you are looking to replace
1413Github with cgit, keep in mind that cgit does not handle issues or
1414pull/merge requests. If people wish to contribute to your work, they
1415would have to send you a patch via email.&lt;/p&gt;
902&lt;h3 id="setup"&gt;Setup&lt;/h3&gt; 1416&lt;h3 id="setup"&gt;Setup&lt;/h3&gt;
903&lt;p&gt;Installing cgit is fairly straightforward, if you would like to compile it from source:&lt;/p&gt; 1417&lt;p&gt;Installing cgit is fairly straightforward, if you would like to
1418compile it from source:&lt;/p&gt;
904&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode sh"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# fetch&lt;/span&gt;&lt;/span&gt; 1419&lt;div class="sourceCode" id="cb1"&gt;&lt;pre class="sourceCode sh"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb1-1"&gt;&lt;a href="#cb1-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# fetch&lt;/span&gt;&lt;/span&gt;
905&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;git&lt;/span&gt; clone https://git.zx2c4.com &lt;span class="kw"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; cgit&lt;/span&gt; 1420&lt;span id="cb1-2"&gt;&lt;a href="#cb1-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;git&lt;/span&gt; clone https://git.zx2c4.com &lt;span class="kw"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="bu"&gt;cd&lt;/span&gt; cgit&lt;/span&gt;
906&lt;span id="cb1-3"&gt;&lt;a href="#cb1-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;git&lt;/span&gt; submodule init&lt;/span&gt; 1421&lt;span id="cb1-3"&gt;&lt;a href="#cb1-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;git&lt;/span&gt; submodule init&lt;/span&gt;
@@ -909,9 +1424,16 @@ H N E I
909&lt;span id="cb1-6"&gt;&lt;a href="#cb1-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# install&lt;/span&gt;&lt;/span&gt; 1424&lt;span id="cb1-6"&gt;&lt;a href="#cb1-6" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# install&lt;/span&gt;&lt;/span&gt;
910&lt;span id="cb1-7"&gt;&lt;a href="#cb1-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;make&lt;/span&gt; NO_LUA=1&lt;/span&gt; 1425&lt;span id="cb1-7"&gt;&lt;a href="#cb1-7" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;make&lt;/span&gt; NO_LUA=1&lt;/span&gt;
911&lt;span id="cb1-8"&gt;&lt;a href="#cb1-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;sudo&lt;/span&gt; make install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1426&lt;span id="cb1-8"&gt;&lt;a href="#cb1-8" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;sudo&lt;/span&gt; make install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
912&lt;p&gt;This would drop the cgit cgi script (and the default css) into &lt;code&gt;/var/www/htdocs/cgit&lt;/code&gt;. You may configure cgit by editing &lt;code&gt;/etc/cgitrc&lt;/code&gt;. I specify the &lt;code&gt;NO_LUA&lt;/code&gt; flag to compile without lua support, exclude that flag if you would like to extend cgit via lua scripts.&lt;/p&gt; 1427&lt;p&gt;This would drop the cgit cgi script (and the default css) into
1428&lt;code&gt;/var/www/htdocs/cgit&lt;/code&gt;. You may configure cgit by editing
1429&lt;code&gt;/etc/cgitrc&lt;/code&gt;. I specify the &lt;code&gt;NO_LUA&lt;/code&gt; flag to
1430compile without lua support, exclude that flag if you would like to
1431extend cgit via lua scripts.&lt;/p&gt;
913&lt;h3 id="going-live"&gt;Going live&lt;/h3&gt; 1432&lt;h3 id="going-live"&gt;Going live&lt;/h3&gt;
914&lt;p&gt;You might want to use, &lt;a href="https://github.com/gnosek/fcgiwrap"&gt;fcgiwrap&lt;/a&gt;, a &lt;a href="http://www.nongnu.org/fastcgi"&gt;fastcgi&lt;/a&gt; wrapper for &lt;code&gt;cgi&lt;/code&gt; scripts,&lt;/p&gt; 1433&lt;p&gt;You might want to use, &lt;a
1434href="https://github.com/gnosek/fcgiwrap"&gt;fcgiwrap&lt;/a&gt;, a &lt;a
1435href="http://www.nongnu.org/fastcgi"&gt;fastcgi&lt;/a&gt; wrapper for
1436&lt;code&gt;cgi&lt;/code&gt; scripts,&lt;/p&gt;
915&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode sh"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;sudo&lt;/span&gt; apt install fcgiwrap&lt;/span&gt; 1437&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode sh"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;sudo&lt;/span&gt; apt install fcgiwrap&lt;/span&gt;
916&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;sudo&lt;/span&gt; systemctl start fcgiwrap.socket&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1438&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="fu"&gt;sudo&lt;/span&gt; systemctl start fcgiwrap.socket&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
917&lt;p&gt;Expose the cgit cgi script to the web via &lt;code&gt;nginx&lt;/code&gt;:&lt;/p&gt; 1439&lt;p&gt;Expose the cgit cgi script to the web via &lt;code&gt;nginx&lt;/code&gt;:&lt;/p&gt;
@@ -935,15 +1457,21 @@ server {
935&lt;p&gt;Point cgit to your git repositories:&lt;/p&gt; 1457&lt;p&gt;Point cgit to your git repositories:&lt;/p&gt;
936&lt;pre&gt;&lt;code&gt;# /etc/cgitrc 1458&lt;pre&gt;&lt;code&gt;# /etc/cgitrc
937scan-path=/path/to/git/repos&lt;/code&gt;&lt;/pre&gt; 1459scan-path=/path/to/git/repos&lt;/code&gt;&lt;/pre&gt;
938&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note&lt;/em&gt;&lt;/strong&gt;: &lt;em&gt;&lt;code&gt;scan-path&lt;/code&gt; works best if you stick it at the end of your &lt;code&gt;cgitrc&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt; 1460&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note&lt;/em&gt;&lt;/strong&gt;: &lt;em&gt;&lt;code&gt;scan-path&lt;/code&gt; works best
939&lt;p&gt;You may now create remote repositories at &lt;code&gt;/path/to/git/repos&lt;/code&gt;, via:&lt;/p&gt; 1461if you stick it at the end of your &lt;code&gt;cgitrc&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;
1462&lt;p&gt;You may now create remote repositories at
1463&lt;code&gt;/path/to/git/repos&lt;/code&gt;, via:&lt;/p&gt;
940&lt;pre&gt;&lt;code&gt;git init --bare&lt;/code&gt;&lt;/pre&gt; 1464&lt;pre&gt;&lt;code&gt;git init --bare&lt;/code&gt;&lt;/pre&gt;
941&lt;p&gt;Add the remote to your local repository:&lt;/p&gt; 1465&lt;p&gt;Add the remote to your local repository:&lt;/p&gt;
942&lt;pre&gt;&lt;code&gt;git remote set-url origin user@remote:/above/path 1466&lt;pre&gt;&lt;code&gt;git remote set-url origin user@remote:/above/path
943git push origin master&lt;/code&gt;&lt;/pre&gt; 1467git push origin master&lt;/code&gt;&lt;/pre&gt;
944&lt;h3 id="configuration"&gt;Configuration&lt;/h3&gt; 1468&lt;h3 id="configuration"&gt;Configuration&lt;/h3&gt;
945&lt;p&gt;cgit is fairly easy to configure, all configuration options can be found &lt;a href="https://git.zx2c4.com/cgit/tree/cgitrc.5.txt"&gt;in the manual&lt;/a&gt;, here are a couple of cool ones though:&lt;/p&gt; 1469&lt;p&gt;cgit is fairly easy to configure, all configuration options can be
946&lt;p&gt;&lt;strong&gt;enable-commit-graph&lt;/strong&gt;: Generates a text based graphical representation of the commit history, similar to &lt;code&gt;git log --graph --oneline&lt;/code&gt;.&lt;/p&gt; 1470found &lt;a href="https://git.zx2c4.com/cgit/tree/cgitrc.5.txt"&gt;in the
1471manual&lt;/a&gt;, here are a couple of cool ones though:&lt;/p&gt;
1472&lt;p&gt;&lt;strong&gt;enable-commit-graph&lt;/strong&gt;: Generates a text based
1473graphical representation of the commit history, similar to
1474&lt;code&gt;git log --graph --oneline&lt;/code&gt;.&lt;/p&gt;
947&lt;pre&gt;&lt;code&gt;| * | Add support for configuration file 1475&lt;pre&gt;&lt;code&gt;| * | Add support for configuration file
948* | | simplify command parsing logic 1476* | | simplify command parsing logic
949* | | Refactor parsers 1477* | | Refactor parsers
@@ -964,7 +1492,11 @@ git push origin master&lt;/code&gt;&lt;/pre&gt;
964| | * | Merge branch &amp;#39;fix/duplicate-habits&amp;#39; 1492| | * | Merge branch &amp;#39;fix/duplicate-habits&amp;#39;
965| | |\ \ 1493| | |\ \
966| | | * | move duplicate check to command parsing blo...&lt;/code&gt;&lt;/pre&gt; 1494| | | * | move duplicate check to command parsing blo...&lt;/code&gt;&lt;/pre&gt;
967&lt;p&gt;&lt;strong&gt;section-from-path&lt;/strong&gt;: This option paired with &lt;code&gt;scan-path&lt;/code&gt; will automatically generate sections in your cgit index page, from the path to each repo. For example, the directory structure used to generate sections on &lt;a href="https://git.peppe.rs"&gt;my cgit instance&lt;/a&gt; looks like this:&lt;/p&gt; 1495&lt;p&gt;&lt;strong&gt;section-from-path&lt;/strong&gt;: This option paired with
1496&lt;code&gt;scan-path&lt;/code&gt; will automatically generate sections in your cgit
1497index page, from the path to each repo. For example, the directory
1498structure used to generate sections on &lt;a href="https://git.peppe.rs"&gt;my
1499cgit instance&lt;/a&gt; looks like this:&lt;/p&gt;
968&lt;pre&gt;&lt;code&gt;├── cli 1500&lt;pre&gt;&lt;code&gt;├── cli
969│ ├── dijo 1501│ ├── dijo
970│ ├── eva 1502│ ├── eva
@@ -988,11 +1520,18 @@ git push origin master&lt;/code&gt;&lt;/pre&gt;
988└── web 1520└── web
989 └── isostatic&lt;/code&gt;&lt;/pre&gt; 1521 └── isostatic&lt;/code&gt;&lt;/pre&gt;
990&lt;h3 id="ease-of-use"&gt;Ease of use&lt;/h3&gt; 1522&lt;h3 id="ease-of-use"&gt;Ease of use&lt;/h3&gt;
991&lt;p&gt;As I mentioned before, &lt;code&gt;cgit&lt;/code&gt; is simply a view into your git repositories, you will have to manually create new repositories by entering your remote and using &lt;code&gt;git init --bare&lt;/code&gt;. Here are a couple of scripts I wrote to perform actions on remotes, think of it as a smaller version of Github’s &lt;code&gt;gh&lt;/code&gt; program.&lt;/p&gt; 1523&lt;p&gt;As I mentioned before, &lt;code&gt;cgit&lt;/code&gt; is simply a view into your
992&lt;p&gt;You may save these scripts as &lt;code&gt;git-script-name&lt;/code&gt; and drop them in your &lt;code&gt;$PATH&lt;/code&gt;, and git will automatically add an alias called &lt;code&gt;script-name&lt;/code&gt;, callable via:&lt;/p&gt; 1524git repositories, you will have to manually create new repositories by
1525entering your remote and using &lt;code&gt;git init --bare&lt;/code&gt;. Here are a
1526couple of scripts I wrote to perform actions on remotes, think of it as
1527a smaller version of Github’s &lt;code&gt;gh&lt;/code&gt; program.&lt;/p&gt;
1528&lt;p&gt;You may save these scripts as &lt;code&gt;git-script-name&lt;/code&gt; and drop
1529them in your &lt;code&gt;$PATH&lt;/code&gt;, and git will automatically add an alias
1530called &lt;code&gt;script-name&lt;/code&gt;, callable via:&lt;/p&gt;
993&lt;pre&gt;&lt;code&gt;git script-name&lt;/code&gt;&lt;/pre&gt; 1531&lt;pre&gt;&lt;code&gt;git script-name&lt;/code&gt;&lt;/pre&gt;
994&lt;h4 id="git-new-repo"&gt;git-new-repo&lt;/h4&gt; 1532&lt;h4 id="git-new-repo"&gt;git-new-repo&lt;/h4&gt;
995&lt;p&gt;Creates a new repository on your remote, the first arg may be a path (section/repo-name) or just the repo name:&lt;/p&gt; 1533&lt;p&gt;Creates a new repository on your remote, the first arg may be a path
1534(section/repo-name) or just the repo name:&lt;/p&gt;
996&lt;pre&gt;&lt;code&gt;#! /usr/bin/env bash 1535&lt;pre&gt;&lt;code&gt;#! /usr/bin/env bash
997# 1536#
998# usage: 1537# usage:
@@ -1009,7 +1548,10 @@ fi
1009 1548
1010ssh user@remote git init --bare &amp;quot;$1&amp;quot;;&lt;/code&gt;&lt;/pre&gt; 1549ssh user@remote git init --bare &amp;quot;$1&amp;quot;;&lt;/code&gt;&lt;/pre&gt;
1011&lt;h4 id="git-set-desc"&gt;git-set-desc&lt;/h4&gt; 1550&lt;h4 id="git-set-desc"&gt;git-set-desc&lt;/h4&gt;
1012&lt;p&gt;To set a one line repository description. It simply copies the local &lt;code&gt;.git/description&lt;/code&gt;, into &lt;code&gt;remote/description&lt;/code&gt;. &lt;code&gt;cgit&lt;/code&gt; displays the contents of this file on the index page:&lt;/p&gt; 1551&lt;p&gt;To set a one line repository description. It simply copies the local
1552&lt;code&gt;.git/description&lt;/code&gt;, into &lt;code&gt;remote/description&lt;/code&gt;.
1553&lt;code&gt;cgit&lt;/code&gt; displays the contents of this file on the index
1554page:&lt;/p&gt;
1013&lt;pre&gt;&lt;code&gt;#! /usr/bin/env bash 1555&lt;pre&gt;&lt;code&gt;#! /usr/bin/env bash
1014# 1556#
1015# usage: 1557# usage:
@@ -1024,11 +1566,24 @@ scp .git/description &amp;quot;$remote/description&amp;quot;&lt;/code&gt;&lt;/pr
1024</item> 1566</item>
1025<item> 1567<item>
1026<title>NixOS</title> 1568<title>NixOS</title>
1027<description>&lt;p&gt;I have been eyeing operating systems with functional package managers for a while now, aka, NixOS or Guix. Reproducible builds, declarative and rollback-able system configuration, system consistency, all sound pretty cool. I have been using NixOS for about a month now.&lt;/p&gt; 1569<description>&lt;p&gt;I have been eyeing operating systems with functional package managers
1570for a while now, aka, NixOS or Guix. Reproducible builds, declarative
1571and rollback-able system configuration, system consistency, all sound
1572pretty cool. I have been using NixOS for about a month now.&lt;/p&gt;
1028&lt;h3 id="installation"&gt;Installation&lt;/h3&gt; 1573&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;
1029&lt;p&gt;I went with their minimal installation ISO. The installation was pretty smooth from start to end, no hitches there. The entire &lt;a href="https://nixos.org/manual/nixos/stable/"&gt;manual&lt;/a&gt; is available offline, and is accessible during the installation. Very handy.&lt;/p&gt; 1574&lt;p&gt;I went with their minimal installation ISO. The installation was
1575pretty smooth from start to end, no hitches there. The entire &lt;a
1576href="https://nixos.org/manual/nixos/stable/"&gt;manual&lt;/a&gt; is available
1577offline, and is accessible during the installation. Very handy.&lt;/p&gt;
1030&lt;h3 id="setup"&gt;Setup&lt;/h3&gt; 1578&lt;h3 id="setup"&gt;Setup&lt;/h3&gt;
1031&lt;p&gt;The entire system is configured via &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;. Wifi, &lt;code&gt;libinput&lt;/code&gt; gestures, audio, locale settings, there are options for literally everything. You can declaratively write down the packages you want installed too. With fresh installs of most distros, I usually fumble with getting things like screen backlight and media keys to work. If I do manage to fix it, I can’t carry it forward to future installations trivially. Getting all my hardware to work on NixOS is as easy as:&lt;/p&gt; 1579&lt;p&gt;The entire system is configured via
1580&lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;. Wifi, &lt;code&gt;libinput&lt;/code&gt;
1581gestures, audio, locale settings, there are options for literally
1582everything. You can declaratively write down the packages you want
1583installed too. With fresh installs of most distros, I usually fumble
1584with getting things like screen backlight and media keys to work. If I
1585do manage to fix it, I can’t carry it forward to future installations
1586trivially. Getting all my hardware to work on NixOS is as easy as:&lt;/p&gt;
1032&lt;pre&gt;&lt;code&gt;{ 1587&lt;pre&gt;&lt;code&gt;{
1033 server.xserver.libinput.enable = true; # touchpad 1588 server.xserver.libinput.enable = true; # touchpad
1034 programs.light.enable = true; # backlight 1589 programs.light.enable = true; # backlight
@@ -1036,37 +1591,77 @@ scp .git/description &amp;quot;$remote/description&amp;quot;&lt;/code&gt;&lt;/pr
1036 networking.wireless.enable = true; # wifi 1591 networking.wireless.enable = true; # wifi
1037}&lt;/code&gt;&lt;/pre&gt; 1592}&lt;/code&gt;&lt;/pre&gt;
1038&lt;h3 id="developing-with-nix"&gt;Developing with Nix&lt;/h3&gt; 1593&lt;h3 id="developing-with-nix"&gt;Developing with Nix&lt;/h3&gt;
1039&lt;p&gt;Nix makes it easy to enter environments that aren’t affected by your system configuration using &lt;code&gt;nix-shell&lt;/code&gt;.&lt;/p&gt; 1594&lt;p&gt;Nix makes it easy to enter environments that aren’t affected by your
1040&lt;p&gt;Builds may be generated by specifying a &lt;code&gt;default.nix&lt;/code&gt; file, and running &lt;code&gt;nix-build&lt;/code&gt;. Conventional package managers require you to specify a dependency list, but there is no guarantee that this list is complete. The package will build on your machine even if you forget a dependency. However, with Nix, packages are installed to &lt;code&gt;/nix/store&lt;/code&gt;, and not global paths such as &lt;code&gt;/usr/bin/...&lt;/code&gt;, if your project builds, it means you have included every last one.&lt;/p&gt; 1595system configuration using &lt;code&gt;nix-shell&lt;/code&gt;.&lt;/p&gt;
1041&lt;p&gt;Issues on most my projects have been “unable to build because &lt;code&gt;libxcb&lt;/code&gt; is missing”, or “this version of &lt;code&gt;openssl&lt;/code&gt; is too old”. Tools like &lt;code&gt;cargo&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; are poor package managers. While they &lt;em&gt;can&lt;/em&gt; guarantee that Rust or Python dependencies are met, they make assumptions about the target system.&lt;/p&gt; 1596&lt;p&gt;Builds may be generated by specifying a &lt;code&gt;default.nix&lt;/code&gt;
1042&lt;p&gt;For example, &lt;a href="https://github.com/nerdypepper/site"&gt;this website&lt;/a&gt; is now built using Nix, anyone using Nix may simply, clone the repository and run &lt;code&gt;./generate.sh&lt;/code&gt;, and it would &lt;em&gt;just work&lt;/em&gt;, while keeping your global namespace clean™:&lt;/p&gt; 1597file, and running &lt;code&gt;nix-build&lt;/code&gt;. Conventional package managers
1043&lt;div class="sourceCode" id="cb2"&gt;&lt;pre class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;#! /usr/bin/env nix-shell&lt;/span&gt;&lt;/span&gt; 1598require you to specify a dependency list, but there is no guarantee that
1599this list is complete. The package will build on your machine even if
1600you forget a dependency. However, with Nix, packages are installed to
1601&lt;code&gt;/nix/store&lt;/code&gt;, and not global paths such as
1602&lt;code&gt;/usr/bin/...&lt;/code&gt;, if your project builds, it means you have
1603included every last one.&lt;/p&gt;
1604&lt;p&gt;Issues on most my projects have been “unable to build because
1605&lt;code&gt;libxcb&lt;/code&gt; is missing”, or “this version of
1606&lt;code&gt;openssl&lt;/code&gt; is too old”. Tools like &lt;code&gt;cargo&lt;/code&gt; and
1607&lt;code&gt;pip&lt;/code&gt; are poor package managers. While they &lt;em&gt;can&lt;/em&gt;
1608guarantee that Rust or Python dependencies are met, they make
1609assumptions about the target system.&lt;/p&gt;
1610&lt;p&gt;For example, &lt;a href="https://github.com/nerdypepper/site"&gt;this
1611website&lt;/a&gt; is now built using Nix, anyone using Nix may simply, clone
1612the repository and run &lt;code&gt;./generate.sh&lt;/code&gt;, and it would &lt;em&gt;just
1613work&lt;/em&gt;, while keeping your global namespace clean™:&lt;/p&gt;
1614&lt;div class="sourceCode" id="cb2"&gt;&lt;pre
1615class="sourceCode bash"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;a href="#cb2-1" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;#! /usr/bin/env nix-shell&lt;/span&gt;&lt;/span&gt;
1044&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;#! nix-shell -i bash -p eva pandoc esh&lt;/span&gt;&lt;/span&gt; 1616&lt;span id="cb2-2"&gt;&lt;a href="#cb2-2" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;#! nix-shell -i bash -p eva pandoc esh&lt;/span&gt;&lt;/span&gt;
1045&lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt; 1617&lt;span id="cb2-3"&gt;&lt;a href="#cb2-3" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;/span&gt;
1046&lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# some bash magic ;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 1618&lt;span id="cb2-4"&gt;&lt;a href="#cb2-4" aria-hidden="true" tabindex="-1"&gt;&lt;/a&gt;&lt;span class="co"&gt;# some bash magic ;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
1047&lt;p&gt;Dependencies are included with the &lt;code&gt;-p&lt;/code&gt; flag, the shell script is executed with an interpreter, specified with the &lt;code&gt;-i&lt;/code&gt; flag.&lt;/p&gt; 1619&lt;p&gt;Dependencies are included with the &lt;code&gt;-p&lt;/code&gt; flag, the shell
1620script is executed with an interpreter, specified with the
1621&lt;code&gt;-i&lt;/code&gt; flag.&lt;/p&gt;
1048&lt;h3 id="impressions"&gt;Impressions&lt;/h3&gt; 1622&lt;h3 id="impressions"&gt;Impressions&lt;/h3&gt;
1049&lt;p&gt;NixOS is by no means, simple. As a newcomer, using Nix was not easy, heck, I had to learn a purely functional, lazy language to just build programs. There is a lot to be desired on the tooling front as well. A well fleshed out LSP plugin would be nice (&lt;a href="https://github.com/nix-community/rnix-lsp"&gt;rnix-lsp looks promisi