Alex Rutar's Blog Personal webpage for math and related topics. https://rutar.org/icon.svg Zola 2026-02-16T00:00:00+00:00 https://rutar.org Fractal geometry and dynamics 2026-02-16T00:00:00+00:00 2026-02-16T00:00:00+00:00 https://rutar.org/teaching/fractal-geometry-and-dynamics/ <p>Here is a <a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-notes.pdf?v=2026-02-16">link to the lecture notes</a>, last updated on Feb 16, 2026.</p> <p>Here is some supplementary material:</p> <ul> <li><a rel="noopener" target="_blank" href="https://rutar.org/notes/box_versus_packing.pdf">Material on packing measure and dimension</a></li> <li><a rel="noopener" target="_blank" href="https://rutar.org/notes/multifractal_duality.pdf">Supplementary on material on multifractal analysis</a></li> </ul> <h3 id="exercise-sheets">Exercise sheets</h3> <p>Exercise 5 typo: the definition of semi-algebra was missing the assumption that it is a finite <em>disjoint</em> union (thanks Mikko for noticing this!)</p> <ul> <li><a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-ex-1.pdf">Exercise 1</a> and <a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-sols-1.pdf">Solutions</a></li> <li><a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-ex-2.pdf">Exercise 2</a> and <a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-sols-2.pdf">Solutions</a></li> <li><a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-ex-3.pdf">Exercise 3</a> and <a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-sols-3.pdf">Solutions</a></li> <li><a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-ex-4.pdf">Exercise 4</a> and <a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-sols-4.pdf">Solutions</a></li> <li><a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-ex-5.pdf">Exercise 5</a> due 12:15pm Thursday February 19</li> </ul> <h3 id="final-project-information">Final project information</h3> <p>The final project will be due on Sunday, February 22 at 23:59. Here is the list of available projects.</p> <ul> <li>Dimensions of self-affine carpets (<strong>reserved for Yibo Chen</strong>)</li> <li>Separation conditions for self-similar sets</li> <li>Inhomogeneous self-similar sets</li> <li>Assouad dimension and weak tangents (<strong>reserved for Mikko Liimatainen</strong>)</li> <li>Multifractal analysis for self-similar measures</li> <li>Topological dynamical systems (<strong>reserved for Ella Suur-Askola</strong>)</li> </ul> <p>For detail on the projects, as well as information about the evaluation, see the <a href="https://rutar.org/teaching/fractal-geometry-and-dynamics/fgd-final-project.pdf">information sheet</a>.</p> <h2 id="important-course-information">Important course information</h2> <h3 id="additional-course-material">Additional course material</h3> <ul> <li><a rel="noopener" target="_blank" href="https://www.math.stonybrook.edu/~bishop/classes/math324.F15/book1Dec15.pdf">Fractals in probability and analysis</a> by Chris Bishop and Yuval Peres.</li> <li><a rel="noopener" target="_blank" href="https://zbmath.org/0869.28003">Techniques in Fractal Geometry</a> by Kenneth Falconer.</li> <li><a rel="noopener" target="_blank" href="https://zbmath.org/1543.28001">Self-similar and self-affine sets and measures</a> by Balázs Bárány, Károly Simon, and Boris Solomyak. Email me if you want a PDF copy.</li> <li>Mike Hochman’s <a rel="noopener" target="_blank" href="https://math.huji.ac.il/~mhochman/courses/ergodic-theory-2017/notes.pdf">ergodic theory notes</a>.</li> <li>Marcelo Viana’s <a rel="noopener" target="_blank" href="https://w3.impa.br/~viana/out/rokhlin.pdf">notes on Rokhlin disintegration</a>.</li> <li>Gerald Folland’s <a rel="noopener" target="_blank" href="https://apachepersonal.miun.se/~andrli/Bok.pdf">real analysis textbook</a>.</li> </ul> <h3 id="evaluation">Evaluation</h3> <p>This course is scored out of 100, but ultimately the evaluation is pass-fail.</p> <p>To pass, you require at least 60 points, with a minimum of 25 points on the final project. The finer breakdown is as follows:</p> <ul> <li>written exercise solutions (40 points; your scores on the assignments will be normalized if the sum is not 40)</li> <li>presentation of the exercises during exercise classes (10 points; 2 per exercise class with a free pass if you miss one)</li> <li>an independent final project with a written (5-10 pages, 25 points) and oral component (45-60 minutes, 25 points).</li> </ul> <p>There will be around 5 available bonus points in the exercises which apply to your overall grade.</p> <h3 id="schedule">Schedule</h3> <ul> <li>Lectures take place from <em>12:15</em> to <em>14:00</em> on Thursdays and Fridays, starting on January 8 and ending on February 20.</li> <li>Exercise classes take place from <em>14:15</em> to <em>16:00</em> on Thursdays, starting on January 16 and ending on February 20.</li> <li>There will be 1 exercise sheet due per week, on January 15, January 22, January 29, Feburary 5, February 12, and February 19.</li> <li>There will be a final project, due on February 22, with presentations between February 22 and February 27.</li> <li>There will be no exam and the course will end on February 27.</li> </ul> <h2 id="course-details">Course details</h2> <h3 id="description">Description</h3> <p>The goal of this course is to provide an introduction to fractal geometry from the perspective of dynamical systems theory. A particular emphasis is given on objects with some form of <em>invariance under re-scaling</em>, such as <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Self-similarity">self-similarity</a>.</p> <p>A key intermediate goal is to introduce <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Dynamical_system">dynamical systems theory</a>, which (for us) is a useful frame of reference for understanding invariance. This is a ubiquitous tool in analysis, PDEs, geometry, mathematical physics, engineering, …</p> <p>Some topics which will be covered:</p> <ul> <li><em>Iterated function systems</em></li> <li><em>Dimension of sets and measures</em></li> <li><em>Measure preserving systems</em></li> <li><em>Ergodic theorems</em></li> <li><em>Conditional measures, Rokhlin disintegration</em></li> <li><em>Exact dimensionality of self-similar measures</em></li> </ul> <p>Related courses:</p> <ul> <li><a rel="noopener" target="_blank" href="https://sites.google.com/view/tuomaths/teaching/real-analysis-fall-2025">Real analysis, Fall 2025</a></li> <li><a rel="noopener" target="_blank" href="https://kfaessler.wixsite.com/math/geometric-measure-theory-2025">Geometric measure theory, Spring 2025</a></li> </ul> <h3 id="pre-requisites">Pre-requisites</h3> <p>The following pre-requisites are essential.</p> <ul> <li>Measure and integration 1 &amp; 2</li> <li>Introductory probability theory</li> </ul> (Un)safely moving elements out of borrowed data 2025-11-19T00:00:00+00:00 2025-11-19T00:00:00+00:00 https://rutar.org/writing/sentinel-pattern-and-panics/ <h2 id="temporarily-moving-elements-out-of-collections"><a href="#temporarily-moving-elements-out-of-collections"></a>Temporarily moving elements out of collections</h2> <p>A recurring pattern when working with collections is the following. Suppose I have a collection, like a <code>Vec&lt;T&gt;</code>, and suppose I would like to take an element out and replace it with a new one.</p> <p>Fortunately for us, holding a mutable reference is almost the same as having ownership, so we can for example use <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/mem/fn.replace.html"><code>replace</code></a>:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_at_index</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">new</span><span class="z-punctuation z-separator z-rust">:</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> T</span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">mem<span class="z-punctuation z-accessor z-rust">::</span></span>replace<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> vec<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">[</span>idx<span class="z-punctuation z-section z-group z-end z-rust">]</span></span><span class="z-punctuation z-separator z-rust">,</span> new</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>What happens if we want the replacement element to depend on the element that we just removed?</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_with</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, F<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; T, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> e <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">remove</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">insert</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx<span class="z-punctuation z-separator z-rust">,</span> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>e</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>This solution is very inefficient! <code>vec.remove</code> removes the element at <code>idx</code>, and then <em>shifts all of the remaining elements left</em>, which we then proceed to <em>immediately shift back</em>. This happens since a <code>Vec</code> must always be contiguous in memory.</p> <p>If we can get away with it, we could pass an <code>&amp;mut T</code> reference in the closure, instead of passing ownership.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">update_with</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, F<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> T<span class="z-punctuation z-section z-group z-end z-rust">)</span>, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> vec<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">[</span>idx<span class="z-punctuation z-section z-group z-end z-rust">]</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>For example, this is the approach that the <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html">entry API</a> takes. However, this still requires having the new element at hand at the point when it is replaced; we’ve just deferred the call to <code>replace</code> farther into the closure.</p> <p>For a more realistic<label for="in1">1</label><input type="checkbox" id="in1"><small>At least this is the example that motivated me to think about this problem.</small> situation analogous to the above, suppose we in fact want to replace the element at <code>idx</code> with an iterator of elements which can depend on the index. Here, the analogy to <code>insert</code> is <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/vec/struct.Vec.html#method.splice"><code>splice</code></a>:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">update_with_iter</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, F, I<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> I<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">IntoIterator</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>Item = T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; I, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> the same inefficient solution </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> new <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">remove</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">splice</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx<span class="z-keyword z-operator z-range z-rust">..</span>idx<span class="z-punctuation z-separator z-rust">,</span> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>new</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>Our goal is to optimize this implementation to avoid shifting the elements twice, and ideally (for example, if the iterator contains exactly one element) to not shift the elements at all.</p> <h3 id="why-do-we-have-to-be-careful"><a href="#why-do-we-have-to-be-careful"></a>Why do we have to be careful?</h3> <p>Let’s do something illegal.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_with</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, F<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; T, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-macro z-rust">assert!</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx <span class="z-keyword z-operator z-comparison z-rust">&lt;</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> please don&#39;t do this </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-modifier z-rust">unsafe</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> the location of the element </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> p <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_mut_ptr</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">add</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> e <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">ptr<span class="z-punctuation z-accessor z-rust">::</span></span>read<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>p</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> new <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>e</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> write the new element without reading or dropping the original value </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">ptr<span class="z-punctuation z-accessor z-rust">::</span></span>write<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>p<span class="z-punctuation z-separator z-rust">,</span> new</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>The short answer is: what happens if the closure panics?</p> <p>If the closure <code>f</code> panics here, the element <code>T</code> could be freed twice: first by the closure itself (which took ownership of <code>T</code>), and then by the <code>Drop</code> implementation of <code>Vec</code>, which cleans up its own memory. The crux of the issue is that a <code>Vec</code> assumes that all of its elements are <em>always</em> in an initialized state.</p> <h3 id="a-safe-solution-using-a-sentinel-value"><a href="#a-safe-solution-using-a-sentinel-value"></a>A safe solution: using a sentinel value</h3> <p>A safe pattern is to use a cheap default value to swap with the element. The idea is the following: first, swap the default value for the element, then do something with the element, and then swap back.</p> <p>For example, if <code>T::default()</code> is cheap (for instance if <code>T</code> is an <code>Option&lt;U&gt;</code>):</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-keyword z-other z-rust">use</span> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">mem<span class="z-punctuation z-accessor z-rust">::</span></span>swap<span class="z-punctuation z-terminator z-rust">;</span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_with</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-support z-type z-rust">Default</span>, F<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; T, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> <span class="z-storage z-modifier z-rust">mut</span> sentinel <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-path z-rust">T<span class="z-punctuation z-accessor z-rust">::</span></span>default<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-function z-rust">swap</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> sentinel<span class="z-punctuation z-separator z-rust">,</span> <span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> vec<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">[</span>idx<span class="z-punctuation z-section z-group z-end z-rust">]</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> <span class="z-storage z-modifier z-rust">mut</span> new <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>sentinel</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-function z-rust">swap</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> new<span class="z-punctuation z-separator z-rust">,</span> <span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> vec<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">[</span>idx<span class="z-punctuation z-section z-group z-end z-rust">]</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>Now if <code>f</code> panics, the original vector could be left in a logically invalid state, but this will not result in memory corruption.</p> <p>What happens if <code>T</code> does not have a cheap default? We can just use other values already in the <code>Vec</code>!</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_with</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, F<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; T, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> x <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">swap_remove</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> last_idx <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">push</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>x</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">swap</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx<span class="z-punctuation z-separator z-rust">,</span> last_idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>Finally, for the <code>replace_iter</code> variant, this is the analogous implementation:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_iter</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>F, T, I<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> I<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">IntoIterator</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>Item = T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; I, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-control z-rust">if</span> idx <span class="z-keyword z-operator z-arithmetic z-rust">+</span> <span class="z-constant z-numeric z-integer z-decimal z-rust">1</span> <span class="z-keyword z-operator z-comparison z-rust">==</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> e <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">pop</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">unwrap</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">extend</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>e</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span> <span class="z-keyword z-control z-rust">else</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> swap with the last element </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> e <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">swap_remove</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> splice in the new elements into the position </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> occupied by the last element </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> last <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> <span class="z-storage z-modifier z-rust">mut</span> iter <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">splice</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx<span class="z-keyword z-operator z-range z-rust">..</span>idx <span class="z-keyword z-operator z-arithmetic z-rust">+</span> <span class="z-constant z-numeric z-integer z-decimal z-rust">1</span><span class="z-punctuation z-separator z-rust">,</span> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>e</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> iter<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">next</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">unwrap</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> put the last element back </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">push</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>last</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>The idea that the data could be left in an invalid state is particularly important in multi-threaded code. If we access the vector through a <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/sync/struct.Mutex.html">mutex</a>, and then our closure panics while calling one of the above <code>replace_with</code> functions, the fact that the data is invalid could result in logic errors in the other thread. This is referred to as <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/sync/struct.Mutex.html#poisoning">poisoning</a>.</p> <p>If you are still interested in the sentinel pattern, Niko Matsakis has a <a rel="noopener" target="_blank" href="https://smallcultfollowing.com/babysteps/blog/2018/11/10/after-nll-moving-from-borrowed-data-and-the-sentinel-pattern/">nice article</a> about it.</p> <h2 id="solutions-using-unsafe-code"><a href="#solutions-using-unsafe-code"></a>Solutions using unsafe code</h2> <p>Now let’s revisit our earlier problems and solve them using unsafe Rust.</p> <h3 id="updating-an-element-in-place-with-a-closure"><a href="#updating-an-element-in-place-with-a-closure"></a>Updating an element in-place with a closure</h3> <p>Let’s start with <code>replace_with</code>. Let’s recalling our issue from before. If the closure panics, the element which we are replacing could be freed twice: once by the closure itself while unwinding, and again by the <code>Vec</code>.</p> <p>A common way to work around this issue is to tell the vector that it is no longer responsible for the memory.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_with</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, F<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; T, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-macro z-rust">assert!</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx <span class="z-keyword z-operator z-comparison z-rust">&lt;</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-modifier z-rust">unsafe</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> original_len <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> now the vector is only responsible for elements 0..idx </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">set_len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> update the element </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> p <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_mut_ptr</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">add</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> e <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">ptr<span class="z-punctuation z-accessor z-rust">::</span></span>read<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>p</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> new <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>e</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">ptr<span class="z-punctuation z-accessor z-rust">::</span></span>write<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>p<span class="z-punctuation z-separator z-rust">,</span> new</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> this is valid since the element at `idx` is once again initialized </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">set_len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>original_len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>Now, if <code>f</code> panics, <code>e</code> is dropped by the closure, and the elements in the vector preceding <code>e</code> are dropped by the <code>Vec</code>.</p> <p>In case of a panic, the elements which follow <code>e</code> will be leaked. This does not cause undefined behaviour; generally speaking leaking is a <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/mem/fn.forget.html">safe operation</a>. However, leaking memory isn’t great if we can avoid it.</p> <h3 id="replacing-an-element-with-an-iterator"><a href="#replacing-an-element-with-an-iterator"></a>Replacing an element with an iterator</h3> <p>Now let’s handle the general case in <code>replace_iter</code>.</p> <p>In order to minimize the amount of unsafe code we have to write, and also to depend on the optimized implementation of <code>splice</code> in the standard library, we can take the following approach:</p> <ol> <li>Start with the approach from <code>replace_with</code>: temporarily set the length, use <code>ptr::read</code> to load the element we want to replace, and then apply the closure to get the new iterator.</li> <li>If the iterator is empty, shift the remaining elements back by 1 index, correct the length, and return. Otherwise, it has at least one element, so we can use <code>ptr::write</code> to put that element back.</li> <li>At this point, the vector is back in an initialized state, so we can use <code>splice</code> to insert the remaining elements at the next index.</li> </ol> <p>This looks like the following in practice:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">replace_iter</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>F, T, I<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">vec</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-rust">mut</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">Vec</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-variable z-parameter z-rust">idx</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-type z-rust">usize</span>, <span class="z-variable z-parameter z-rust">f</span><span class="z-punctuation z-separator z-rust">:</span> F</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> I<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">IntoIterator</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>Item = T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"> F<span class="z-punctuation z-separator z-rust">:</span> FnOnce<span class="z-punctuation z-section z-group z-begin z-rust">(</span>T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; I, </span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"></span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-macro z-rust">assert!</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx <span class="z-keyword z-operator z-comparison z-rust">&lt;</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-separator z-rust">,</span> <span class="z-string z-quoted z-double z-rust"><span class="z-punctuation z-definition z-string z-begin z-rust">&quot;</span>index out of bounds<span class="z-punctuation z-definition z-string z-end z-rust">&quot;</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-modifier z-rust">unsafe</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> original_len <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> the place we are removing from </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> ptr <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_mut_ptr</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">add</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> item <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">ptr<span class="z-punctuation z-accessor z-rust">::</span></span>read<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>ptr</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> temporarily decrease length to avoid double-free if the </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> closure or some iterator method panics </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">set_len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> get the replacement iterator </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> <span class="z-storage z-modifier z-rust">mut</span> iter <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-support z-function z-rust">f</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>item</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">into_iter</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-control z-rust">match</span> iter<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">next</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-type z-rust">None</span> <span class="z-keyword z-operator z-rust">=&gt;</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> empty iterator - shift everything down to fill in the original spot </span></span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">ptr<span class="z-punctuation z-accessor z-rust">::</span></span>copy<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>ptr<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">add</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-constant z-numeric z-integer z-decimal z-rust">1</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-separator z-rust">,</span> ptr<span class="z-punctuation z-separator z-rust">,</span> original_len <span class="z-keyword z-operator z-arithmetic z-rust">-</span> idx <span class="z-keyword z-operator z-arithmetic z-rust">-</span> <span class="z-constant z-numeric z-integer z-decimal z-rust">1</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">set_len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>original_len <span class="z-keyword z-operator z-arithmetic z-rust">-</span> <span class="z-constant z-numeric z-integer z-decimal z-rust">1</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-type z-rust">Some</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>first</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> <span class="z-keyword z-operator z-rust">=&gt;</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> put the element back </span></span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-path z-rust">std<span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-path z-rust">ptr<span class="z-punctuation z-accessor z-rust">::</span></span>write<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>ptr<span class="z-punctuation z-separator z-rust">,</span> first</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> the vec is once again in an initialized state so we can reset the length </span></span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">set_len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>original_len</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> splice in the remaining elements </span></span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">let</span> <span class="z-keyword z-operator z-rust">_</span> <span class="z-keyword z-operator z-assignment z-rust">=</span> vec<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">splice</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>idx <span class="z-keyword z-operator z-arithmetic z-rust">+</span> <span class="z-constant z-numeric z-integer z-decimal z-rust">1</span><span class="z-keyword z-operator z-range z-rust">..</span>idx <span class="z-keyword z-operator z-arithmetic z-rust">+</span> <span class="z-constant z-numeric z-integer z-decimal z-rust">1</span><span class="z-punctuation z-separator z-rust">,</span> iter</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span> </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> Dynamically set Cargo features in Neovim 2025-09-12T00:00:00+00:00 2025-09-12T00:00:00+00:00 https://rutar.org/writing/rust-analyzer-dynamic-features/ <h2 id="rust-analyzer"><a href="#rust-analyzer"></a>Rust analyzer</h2> <p><a rel="noopener" target="_blank" href="https://rust-analyzer.github.io/">Rust analyzer</a> is a <a rel="noopener" target="_blank" href="https://microsoft.github.io/language-server-protocol/">language server</a> which provides editor support for <a rel="noopener" target="_blank" href="https://www.rust-lang.org/">the Rust programming language</a>. <a rel="noopener" target="_blank" href="https://neovim.io/">Neovim</a> has built-in language server protocol support.</p> <p><a rel="noopener" target="_blank" href="https://doc.rust-lang.org/cargo/">Cargo</a> supports feature flags, which enable conditional configuration. However, any blocks which are not compiled for a given feature do not benefit from rust-analyzer language server support.</p> <p>While it is possible to enable features at startup in Neovim configuration, an ideal solution would be to define a user command, say <code>:FtSet</code>, which would set the feature list to the provided arguments. For example, to set features <code>a</code> and <code>b</code> we would like to run <code>:FtSet a b</code>.</p> <p>If you just want the function, you can find it <a href="https://rutar.org/writing/rust-analyzer-dynamic-features/#function-definitions">here</a>. If you are also interested in some Neovim LSP implementation details, read on!</p> <h3 id="required-setup"><a href="#required-setup"></a>Required setup</h3> <p>To set up rust-analyzer, it is convenient to use the <a rel="noopener" target="_blank" href="https://github.com/neovim/nvim-lspconfig"><code>neovim/nvim-lspconfig</code></a> package, which you can install by following the instructions in the repository. You also must have the rust-analyzer binary available on your <code>PATH</code> or you can provide a custom binary during configuration (as detailed <a rel="noopener" target="_blank" href="https://github.com/neovim/nvim-lspconfig?tab=readme-ov-file#quickstart">here</a>).</p> <h3 id="basic-feature-configuration"><a href="#basic-feature-configuration"></a>Basic feature configuration</h3> <p>When enabling rust-analyzer with <code>vim.lsp.enable('rust_analyzer')</code>, it is possible to configure rust-analyzer to enable (or disable) features:</p> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">config</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">settings</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">cargo</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span></span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">features</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span><span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-value z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span></span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-field z-lua">,</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-lua"> <span class="z-punctuation z-section z-block z-end z-lua">}</span></span></span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-field z-lua">,</span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-value z-lua"><span class="z-meta z-mapping z-lua"> <span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-mapping z-value z-lua"></span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></code></pre> <p>However, assuming you have set this up in global configuration like <code>init.lua</code>, it is rather inconvenient to change the features which are enabled while editing a file.</p> <h2 id="dynamic-feature-flags"><a href="#dynamic-feature-flags"></a>Dynamic feature flags</h2> <p>Instead, we want to implement a function to set feature flags dynamically at runtime.</p> <h3 id="modifying-the-existing-configuration"><a href="#modifying-the-existing-configuration"></a>Modifying the existing configuration</h3> <p>In order to update rust-analyzer, we first need to access the existing configuration so that we can modify it without altering other settings. The built-in way to access running language servers is with the <a rel="noopener" target="_blank" href="https://neovim.io/doc/user/lsp.html#vim.lsp.get_clients%28%29"><code>vim.lsp.get_clients</code></a> function. This function accepts an optional dictionary which can be used to filter the clients which are returned. In our case, we would use <code>vim.lsp.get_clients({ name = "rust_analyzer" })</code>.</p> <p>Since this could match multiple clients, this returns an <a rel="noopener" target="_blank" href="https://www.lua.org/pil/11.1.html">array</a>. To access the running client, we can index into the array (by default, lua arrays start at <code>1</code>).</p> <p>To view the configuration of the currently running rust-analyzer server, we would therefore run</p> <pre data-lang="vim" class="language-vim z-code"><code class="language-vim" data-lang="vim"><span class="z-source z-viml">:<span class="z-support z-function z-viml">lua</span>= vim<span class="z-storage z-function z-viml">.</span>lsp<span class="z-storage z-function z-viml">.</span><span class="z-support z-function z-any-method">get_clients</span>({ name = <span class="z-string z-quoted z-double z-viml">&quot;rust_analyzer&quot;</span> })[<span class="z-constant z-numeric z-integer">1</span>]<span class="z-storage z-function z-viml">.</span>config </span></code></pre> <p>In order to update the configuration, we just need to modify the configuration, and then send the updated configuration to rust-analyzer. For example, to set features <code>a</code> and <code>b</code>, we can do something like:</p> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-storage z-modifier z-lua">local</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">get_clients</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">name</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-constant z-numeric z-integer z-decimal z-lua">1</span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">config</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">settings</span> </span><span class="z-source z-lua"><span class="z-comment z-line z-lua"><span class="z-punctuation z-definition z-comment z-lua">--</span> if rust-analyzer is not running at all, this will be `nil` </span></span><span class="z-source z-lua"><span class="z-keyword z-control z-conditional z-lua">if</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-comparison z-lua">~=</span> <span class="z-constant z-language z-null z-lua">nil</span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">cargo</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">features</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>a<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-field z-lua">,</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>b<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span> <span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-comment z-line z-lua"><span class="z-punctuation z-definition z-comment z-lua">--</span> ?? </span></span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"><span class="z-keyword z-control z-end z-lua">end</span></span> </span></code></pre> <p>The structure of the <code>rustAnalyzerSettings</code> object is the same as the object which we initially configured using <code>vim.lsp.config</code>.</p> <h3 id="propogating-the-feature-flags-to-rust-analyzer"><a href="#propogating-the-feature-flags-to-rust-analyzer"></a>Propogating the feature flags to rust-analyzer</h3> <p>Now, the local <code>rustAnalyzerSettings</code> object contains a copy of the configuration, with the <code>cargo.features</code> value updated. However, in order for our configuration update to take place, we must propagate the settings to the (currently running) rust-analyzer instance.</p> <p>The language server protocol has a specific notification type called <a rel="noopener" target="_blank" href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeConfiguration"><code>workspace/didChangeConfiguration</code></a>. This is a notification that is sent by the client to the server. Neovim allows you to send these notifications in lua code with <a rel="noopener" target="_blank" href="https://neovim.io/doc/user/lsp.html#vim.lsp.buf_notify%28%29"><code>vim.lsp.buf_notify</code></a>.</p> <p>In principle, the following should work:</p> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-variable z-other z-lua">let</span> <span class="z-variable z-other z-lua">client</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">get_clients</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">name</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-constant z-numeric z-integer z-decimal z-lua">1</span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span> </span><span class="z-source z-lua"><span class="z-keyword z-control z-conditional z-lua">if</span> <span class="z-variable z-other z-lua">client</span> <span class="z-keyword z-operator z-comparison z-lua">~=</span> <span class="z-constant z-language z-null z-lua">nil</span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-storage z-modifier z-lua">local</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">client</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">config</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">settings</span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">cargo</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">features</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>a<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-field z-lua">,</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>b<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span> <span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">client</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">notify</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>workspace/didChangeConfiguration<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">settings</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"><span class="z-keyword z-control z-end z-lua">end</span></span> </span></code></pre> <p>However, at the time of writing this article, I was unable to make this work. It seems that rust-analyzer and Neovim do not support dynamic updates of <code>workspace/didChangeConfiguration</code>. A hint that this is the case can see this by looking at the ‘capabilities’ section of rust-analyzer:</p> <pre data-lang="vim" class="language-vim z-code"><code class="language-vim" data-lang="vim"><span class="z-source z-viml">:<span class="z-support z-function z-viml">lua</span>= vim<span class="z-storage z-function z-viml">.</span>lsp<span class="z-storage z-function z-viml">.</span><span class="z-support z-function z-any-method">get_clients</span>({ name = <span class="z-string z-quoted z-double z-viml">&quot;rust_analyzer&quot;</span> })[<span class="z-constant z-numeric z-integer">1</span>]<span class="z-storage z-function z-viml">.</span>capabilities<span class="z-storage z-function z-viml">.</span>workspace<span class="z-storage z-function z-viml">.</span>didChangeConfiguration </span></code></pre> <p>We can see here that the <code>dynamicRegistration</code> option is <code>false</code>. As far as I can tell, rust-analyzer by default only requests configuration on startup from Neovim.</p> <h3 id="workaround-restart-rust-analyzer-on-feature-change"><a href="#workaround-restart-rust-analyzer-on-feature-change"></a>Workaround: restart rust-analyzer on feature change</h3> <p>A somewhat unsatisfying but functional workaround is simply to restart rust-analyzer and provide it with the new configuration.</p> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-storage z-modifier z-lua">local</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">get_clients</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">name</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-constant z-numeric z-integer z-decimal z-lua">1</span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">config</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">settings</span> </span><span class="z-source z-lua"><span class="z-keyword z-control z-conditional z-lua">if</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-comparison z-lua">~=</span> <span class="z-constant z-language z-null z-lua">nil</span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">cargo</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">features</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>a<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-field z-lua">,</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>b<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span> <span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-comment z-line z-lua"><span class="z-punctuation z-definition z-comment z-lua">--</span> restart rust-analyzer with new settings </span></span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-constant z-language z-boolean z-true z-lua">false</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">config</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">settings</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span><span class="z-source z-lua"><span class="z-meta z-block z-lua"><span class="z-keyword z-control z-end z-lua">end</span></span> </span></code></pre> <p>All that remains is to wrap this in a user command.</p> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">api</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">nvim_create_user_command</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>FtSet<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-storage z-type z-function z-lua">function</span><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-variable z-parameter z-function z-lua">opts</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-storage z-modifier z-lua">local</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">get_clients</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">name</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-constant z-numeric z-integer z-decimal z-lua">1</span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">config</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">settings</span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-conditional z-lua">if</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-comparison z-lua">~=</span> <span class="z-constant z-language z-null z-lua">nil</span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">cargo</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">features</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">opts</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">fargs</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-constant z-language z-boolean z-true z-lua">false</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">config</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">settings</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">desc</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>Set rust-analyzer features<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span></span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-field z-lua">,</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">nargs</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>*<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></code></pre> <p>Here, <code>opts.fargs</code> is an array of all of the arguments passed to the command <code>:FtSet</code>. We explicitly set <code>nargs = '*'</code> to set an arbitrary number of features simultaneously. You can read more about <code>nvim_create_user_command</code> in the <a rel="noopener" target="_blank" href="https://neovim.io/doc/user/api.html#nvim_create_user_command%28%29">docs</a>.</p> <h2 id="function-definitions"><a href="#function-definitions"></a>Function definitions</h2> <p>Here are a few examples demonstrating some operations. A few things that would be great to fix:</p> <ol> <li>The operations do not de-duplicate the feature list.</li> <li>The implementation is quite repetitive.</li> <li>The arguments should suggest completions from a list of features read from <code>Cargo.toml</code>.</li> <li>You should put these function definitions somewhere that is only enabled when rust-analyzer attaches to the Neovim instance.</li> </ol> <h3 id="set-features"><a href="#set-features"></a>Set features</h3> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">api</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">nvim_create_user_command</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>FtSet<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-storage z-type z-function z-lua">function</span><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-variable z-parameter z-function z-lua">opts</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-storage z-modifier z-lua">local</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">get_clients</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">name</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-constant z-numeric z-integer z-decimal z-lua">1</span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">config</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">settings</span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-conditional z-lua">if</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-comparison z-lua">~=</span> <span class="z-constant z-language z-null z-lua">nil</span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">cargo</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">features</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">opts</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">fargs</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-constant z-language z-boolean z-true z-lua">false</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">config</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">settings</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">desc</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>Set rust-analyzer features to the provided list<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span></span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-field z-lua">,</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">nargs</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>*<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></code></pre> <h3 id="set-all-features-all-features"><a href="#set-all-features-all-features"></a>Set all features (<code>--all-features</code>)</h3> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">api</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">nvim_create_user_command</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>FtSetAll<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-storage z-type z-function z-lua">function</span><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-variable z-parameter z-function z-lua">opts</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-storage z-modifier z-lua">local</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">get_clients</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">name</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-constant z-numeric z-integer z-decimal z-lua">1</span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">config</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">settings</span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-conditional z-lua">if</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-comparison z-lua">~=</span> <span class="z-constant z-language z-null z-lua">nil</span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">cargo</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">features</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>all<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-constant z-language z-boolean z-true z-lua">false</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">config</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">settings</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">enable</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">desc</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>Set all rust-analyzer features<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span></span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-field z-lua">,</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">nargs</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-constant z-numeric z-integer z-decimal z-lua">0</span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></code></pre> <h3 id="list-enabled-features"><a href="#list-enabled-features"></a>List enabled features</h3> <pre data-lang="lua" class="language-lua z-code"><code class="language-lua" data-lang="lua"><span class="z-source z-lua"><span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">api</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">nvim_create_user_command</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>FtList<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-storage z-type z-function z-lua">function</span><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-variable z-parameter z-function z-lua">opts</span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-storage z-modifier z-lua">local</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-assignment z-lua">=</span> <span class="z-variable z-other z-lua">vim</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">lsp</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-variable z-function z-lua">get_clients</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">name</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust_analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-constant z-numeric z-integer z-decimal z-lua">1</span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">config</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">settings</span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-conditional z-lua">if</span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-comparison z-lua">==</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>all<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-support z-function z-builtin z-lua">print</span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>all features enabled<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-conditional z-lua">elseif</span></span> <span class="z-variable z-other z-lua">rustAnalyzerSettings</span> <span class="z-keyword z-operator z-comparison z-lua">~=</span> <span class="z-constant z-language z-null z-lua">nil</span> <span class="z-meta z-block z-lua"><span class="z-keyword z-control z-conditional z-lua">then</span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-support z-function z-builtin z-lua">print</span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>[<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-keyword z-operator z-concatenation z-lua">..</span><span class="z-support z-constant z-builtin z-lua">table</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua"><span class="z-support z-function z-builtin z-lua">concat</span></span><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-begin z-lua">(</span><span class="z-variable z-other z-lua">rustAnalyzerSettings</span><span class="z-meta z-brackets z-lua"><span class="z-punctuation z-section z-brackets z-begin z-lua">[</span><span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>rust-analyzer<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span><span class="z-punctuation z-section z-brackets z-end z-lua">]</span></span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">cargo</span><span class="z-punctuation z-accessor z-lua">.</span><span class="z-meta z-property z-lua">features</span><span class="z-punctuation z-separator z-comma z-lua">,</span> <span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>, <span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span><span class="z-keyword z-operator z-concatenation z-lua">..</span><span class="z-string z-quoted z-single z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&#39;</span>]<span class="z-punctuation z-definition z-string z-end z-lua">&#39;</span></span><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span> </span></span></span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-meta z-function z-lua"><span class="z-meta z-block z-lua"> <span class="z-keyword z-control z-end z-lua">end</span></span></span><span class="z-punctuation z-separator z-comma z-lua">,</span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"> <span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-begin z-lua">{</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">desc</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-string z-quoted z-double z-lua"><span class="z-punctuation z-definition z-string z-begin z-lua">&quot;</span>List rust-analyzer active features.<span class="z-punctuation z-definition z-string z-end z-lua">&quot;</span></span></span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-field z-lua">,</span> </span><span class="z-meta z-mapping z-key z-lua"><span class="z-string z-unquoted z-key z-lua">nargs</span></span><span class="z-meta z-mapping z-lua"> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-separator z-key-value z-lua">=</span></span><span class="z-meta z-mapping z-value z-lua"> <span class="z-constant z-numeric z-integer z-decimal z-lua">0</span> </span><span class="z-meta z-mapping z-lua"><span class="z-punctuation z-section z-block z-end z-lua">}</span></span> </span></span></span><span class="z-source z-lua"><span class="z-meta z-function-call z-arguments z-lua"><span class="z-meta z-group z-lua"><span class="z-punctuation z-section z-group z-end z-lua">)</span></span></span> </span></code></pre> Using Closure-style Traits to Simplify a Rust API 2024-12-04T00:00:00+00:00 2024-12-04T00:00:00+00:00 https://rutar.org/writing/using-closure-traits-to-simplify-rust-api/ <h2 id="introduction"><a href="#introduction"></a>Introduction</h2> <p>I recently published a <a rel="noopener" target="_blank" href="https://www.rust-lang.org/">Rust</a> library called <a rel="noopener" target="_blank" href="https://docs.rs/nucleo-picker/latest/nucleo_picker"><code>nucleo-picker</code></a> which enables command-line applications to incorporate an interface for making a selection from a number of provided options. The selection is done through an interactive query, with a search algorithm used to filter and rank the given possibilities. A popular choice, and the one internal to <code>nucleo-picker</code><label for="in1">1</label><input type="checkbox" id="in1"><small>Not my implementation, but rather from Pascal Kuthe’s <a rel="noopener" target="_blank" href="https://docs.rs/nucleo/latest/nucleo"><code>nucleo</code></a> crate.</small>, is to use the <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Smith%E2%80%93Waterman_algorithm">Smith–Waterman algorithm</a> from DNA sequence alignment since it is relatively simple to implement, performant even in the presence of a large number of matches, and not too sensitive to typos and other user input errors.</p> <p>In our case, since we are implementing a library, we do not make any choices concerning rendering: instead, the library user must define how their types should be represented within the picker. In the end, I decided on the following closure-style trait with generic associated types.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-trait z-rust">trait</span> <span class="z-entity z-name z-trait z-rust">Render</span>&lt;T&gt; <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Str</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-other z-rust">where</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>I would like to discuss my thought process behind this choice, and mainly why this API differs from the much more popular <a rel="noopener" target="_blank" href="https://docs.rs/skim/latest/skim">skim</a> picker library. Instead of just explaining the benefits of such a choice, let’s work our way through some alternative approaches and see how this solution arises.</p> <h2 id="no-generics"><a href="#no-generics"></a>No generics</h2> <p>The first possibility, and certainly the simplest possible choice, is simply to have a concrete type within the picker that the item must be rendered as. For instance, one might require that all items are sent to the picker as a <code>String</code>, or some other internal representation. Internally, for performance reasons, this can be quite ideal: since a matcher engine must perform a large number of score computations every time the query string changes, having an optimized internal representation can greatly improve performance.</p> <p>However, there are quite a few issues with this approach.</p> <h3 id="overhead-to-keep-track-of-items"><a href="#overhead-to-keep-track-of-items"></a>Overhead to keep track of items</h3> <p>The first issue is that the user needs to keep track of the items by themselves. There can be quite a bit of implementation overhead to doing this, especially if for performance you want multi-threading: the user must maintain an internal global item queue (such as an <a rel="noopener" target="_blank" href="https://crates.io/crates/boxcar">append-only vector</a>) and track the association between the items and the rendered format. This is something we would like to deal with on behalf of the user.</p> <h3 id="conversion-overhead-and-api-commitment"><a href="#conversion-overhead-and-api-commitment"></a>Conversion overhead and API commitment</h3> <p>If you have a custom internal type which requires pre-processing to generate (such as a <code>Vec&lt;char&gt;</code>), the user must convert to your provided type. You can provide adapters for your type, such as <code>From&lt;String&gt;</code>; however, the user must still import your type, understand the subtleties, and track down the relevant adapter. Moreover, this is an additional API commitment if your internal representation changes, since you must continue to support the previous conversion methods.</p> <h3 id="extra-memory-overhead-with-lossy-representation"><a href="#extra-memory-overhead-with-lossy-representation"></a>Extra memory overhead with lossy representation</h3> <p>In many situations, internally one does not actually want to store the rendered value, but rather a representation which is optimized for matching. For example, it is often quite ideal to begin with a string slice, iterate over <a rel="noopener" target="_blank" href="https://unicode.org/reports/tr29/">graphemes</a>, and then <a rel="noopener" target="_blank" href="https://unicode.org/reports/tr15/">normalize the grapheme</a> to avoid issues with non-canonical representation. In particular, it may be insufficient to only store the internal representation, and the picker may require two representations: the first, which is a user-friendly rendered format (such as <code>String</code>), and the second, which is an internal representation only used for matching. This unnecessarily incurs additional memory overhead.</p> <h2 id="a-trait-for-picker-items"><a href="#a-trait-for-picker-items"></a>A trait for picker items</h2> <p>We’ve now decided that we want to keep track of the items on behalf of the user and maintain our own internal representation, so the picker is now generic over an item type <code>T</code>. In order for the user to explain how to render the item <code>T</code>, perhaps the first solution that many people will reach for is to require that <code>T</code> implements a trait such as</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-storage z-type z-trait z-rust">trait</span> <span class="z-entity z-name z-trait z-rust">Item</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">text</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>&#39;<span class="z-keyword z-operator z-rust">_</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>This is the approach taken by <a rel="noopener" target="_blank" href="https://docs.rs/skim/latest/skim/trait.SkimItem.html">skim</a>. Then, the picker is generic over any type <code>T</code> which implements <code>Item</code>, and then uses the <code>Item</code> implementation to generate the internal representation, and also to represent the item within the picker view (if the internal representation is lossy).</p> <h3 id="implementation-on-foreign-types"><a href="#implementation-on-foreign-types"></a>Implementation on foreign types</h3> <p>The first concern, which is more of an inconvenience rather than a serious problem, is that an <code>Item</code> trait increases the number of wrapper types if a user wants to have a picker item which is not local to the crate. This is a relatively common concern for the picker: for example, if you wanted to implement a <code>find</code> clone with a picker interface on the matching items, you might import the <a rel="noopener" target="_blank" href="https://docs.rs/ignore/latest/ignore/">ignore</a> crate, and then the natural choice for an item is a <code>DirEntry</code>.</p> <h3 id="difficulty-in-default-implementation"><a href="#difficulty-in-default-implementation"></a>Difficulty in default implementation</h3> <p>In order to alleviate the user responsibility, one could in principle preemptively implement <code>Item</code> for as many foreign types as possible. For instance (and again, this is done by <a rel="noopener" target="_blank" href="https://docs.rs/skim/latest/skim/trait.SkimItem.html">skim</a>), one might have an implementation such as</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> Item <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">T</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">text</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>&#39;<span class="z-keyword z-operator z-rust">_</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-path z-rust">Cow<span class="z-punctuation z-accessor z-rust">::</span></span>Borrowed<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_ref</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>A downside of this approach is that it will result in conflicting implementations if the user’s type already implements <code>AsRef&lt;str&gt;</code> but they wish for the rendered value to be different than the default <code>str</code> implementation.</p> <p>Moreover, to avoid conflicting implementations, one cannot have a second implementation, such as</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>P<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>Path<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> Item <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">P</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">text</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>&#39;<span class="z-keyword z-operator z-rust">_</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_ref</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">to_string_lossy</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>since in principle a type could be <code>AsRef&lt;str&gt; + AsRef&lt;Path&gt;</code>; for example, this is the case for <code>String</code>. Therefore, one must make such a choice concerning the generic implementation, which may be inconvenient for a downstream user who would benefit from a different default implementation.</p> <p>Again using newtypes, it is possible to work around this problem: we could implement a custom wrapper type <code>PathItem</code> and implement <code>Item</code> for it:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-generic z-rust"><span class="z-entity z-name z-struct z-rust">PathItem</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>P<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-meta z-struct z-rust"> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">inner</span><span class="z-punctuation z-separator z-type z-rust">:</span> P </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>P<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>Path<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">PathItem</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>P<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">new</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-variable z-parameter z-rust">inner</span><span class="z-punctuation z-separator z-rust">:</span> P</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-storage z-type z-rust">Self</span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-rust">Self</span> <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> inner </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>P<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>Path<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> Item <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">PathItem</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>P<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">text</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>&#39;<span class="z-keyword z-operator z-rust">_</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span>inner<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_ref</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">to_string_lossy</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>But then the user must wrap their internal type <code>P</code> with <code>PathItem</code> before passing it to the <code>Picker</code>, and then unwrap the item when it is returned.</p> <h3 id="inflexibility-with-stateful-rendering-patterns"><a href="#inflexibility-with-stateful-rendering-patterns"></a>Inflexibility with stateful rendering patterns</h3> <p>The final concern, which is more serious, is that a library user may want rendering to also depend on additional dynamic state. For example, the picker item might be a <code>HashMap</code>, and the library user might wish to only render a subset of the keys, but still retain all of the <code>HashMap</code> data when the item is picked. Implementing this with an <code>Item</code> then requires modifying the item type <code>T</code> to contain the required rendering state, which could either result in substantial overhead or lifetime headaches.</p> <p>In the <code>HashMap</code> example, one would have to do something like</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-entity z-name z-struct z-rust">SubsetHashMap</span> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">map</span><span class="z-punctuation z-separator z-type z-rust">:</span> <span class="z-meta z-generic z-rust">BTreeMap<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-support z-type z-rust">String</span>, <span class="z-support z-type z-rust">String</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">allowed_fields</span><span class="z-punctuation z-separator z-type z-rust">:</span> <span class="z-meta z-generic z-rust">Arc<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-meta z-generic z-rust">HashSet<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-support z-type z-rust">String</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span> </span><span class="z-meta z-impl z-rust">Item <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">SubsetHashMap</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">text</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>&#39;<span class="z-keyword z-operator z-rust">_</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-path z-rust">Cow<span class="z-punctuation z-accessor z-rust">::</span></span>Owned<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span>map </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-group z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">iter</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-group z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">filter</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">|</span></span></span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-variable z-parameter z-rust">k</span><span class="z-punctuation z-separator z-rust">,</span> _</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-section z-parameters z-end z-rust">|</span></span> </span><span class="z-meta z-function z-closure z-rust"><span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span>allowed_fields<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">contains</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>k<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_str</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-group z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">map</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">|</span></span></span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust">_<span class="z-punctuation z-separator z-rust">,</span> <span class="z-variable z-parameter z-rust">v</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-section z-parameters z-end z-rust">|</span></span> </span><span class="z-meta z-function z-closure z-rust">v</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-group z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">join</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-string z-quoted z-double z-rust"><span class="z-punctuation z-definition z-string z-begin z-rust">&quot;</span>, <span class="z-punctuation z-definition z-string z-end z-rust">&quot;</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-group z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">into</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>but now we have the extra (unnecessary) reference counting overhead, along with a rather in-elegant <code>SubsetHashMap</code>.</p> <h2 id="a-closure-for-rendering"><a href="#a-closure-for-rendering"></a>A closure for rendering</h2> <p>After understanding the issues with the <code>Item</code> trait, it seems that we should take a different perspective on the implementation: instead of asking for the item <code>T</code> to describe how it should be rendered, we should instead ask the user to specify a <em>second</em> type <code>R</code> which knows how to render <code>T</code>. In other words, <code>R</code> is a <em>rendering function</em>, so a natural choice for such a type is a <em>closure</em>. In our case, the natural trait bound is something like</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-keyword z-control z-rust">for</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span> <span class="z-support z-type z-rust">Fn</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></code></pre> <p>Now, we ask the library user to pass a rendering function directly to the picker when it is instantiated, rather than define rendering per-item.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-generic z-rust"><span class="z-entity z-name z-struct z-rust">Picker</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-meta z-struct z-rust"> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">renderer</span><span class="z-punctuation z-separator z-type z-rust">:</span> R, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">_marker</span><span class="z-punctuation z-separator z-type z-rust">:</span> <span class="z-meta z-generic z-rust">PhantomData<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> just a placeholder to make this example compile </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">Picker</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"></span><span class="z-meta z-impl z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"> R<span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-other z-rust">for</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> Fn<span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"></span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> now we can internally render items for display </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span>renderer</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>item</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>This is quite close to an optimal solution and it addresses the key issues above.</p> <ol> <li>Implementation on foreign types requires minimal boilerplate, since we just need to write the appropriate function.</li> <li>The closure itself can own the relevant state by using <code>move</code>.</li> <li>There is minimal memory overhead, since all of the required state is held inside the closure, which is only instantiated once.</li> </ol> <p>This implementation basically de-sugars to our original <code>Render</code> trait with the concrete <code>Cow&lt;'a, str&gt;</code> return type. However, there is still one key issue with ergonomics that we would like to overcome.</p> <h3 id="inflexible-return-type"><a href="#inflexible-return-type"></a>Inflexible return type</h3> <p>In the above examples, we mandated a <code>Cow&lt;'_, str&gt;</code> return type, which is generally speaking quite sensible. However, we might wish to allow the user even more flexibility and decide what the return type should be, as long as we can extract the desired data from it. For instance, we would like the closure to be able to return any type which is <code>AsRef&lt;str&gt;</code>. Let’s try to implement this by doing something a bit silly.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-generic z-rust"><span class="z-entity z-name z-struct z-rust">Picker</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-meta z-struct z-rust"> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">renderer</span><span class="z-punctuation z-separator z-type z-rust">:</span> R, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">_marker</span><span class="z-punctuation z-separator z-type z-rust">:</span> <span class="z-meta z-generic z-rust">PhantomData<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R, S<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">Picker</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"></span><span class="z-meta z-impl z-rust"><span class="z-keyword z-other z-rust">where</span> </span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"> S<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"> R<span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-other z-rust">for</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> Fn<span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T<span class="z-punctuation z-section z-group z-end z-rust">)</span> -&gt; S, </span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"></span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> S</span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> now we can internally render items for display </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span>renderer</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>item</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>Well, let’s try it out:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-path z-rust">Picker<span class="z-punctuation z-accessor z-rust">::</span></span>new<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">|</span></span></span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-variable z-parameter z-rust">s</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span>String<span class="z-punctuation z-section z-parameters z-end z-rust">|</span></span> </span><span class="z-meta z-function z-closure z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-separator z-rust">:</span><span class="z-punctuation z-separator z-rust">:</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-path z-rust"><span class="z-punctuation z-accessor z-rust">::</span></span>as_ref<span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>s</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></code></pre> <p>Unfortunately, that doesn’t work:</p> <pre data-lang="txt" class="language-txt z-code"><code class="language-txt" data-lang="txt"><span class="z-text z-plain">error: lifetime may not live long enough </span><span class="z-text z-plain"> --&gt; src/lib.rs:59:31 </span><span class="z-text z-plain"> | </span><span class="z-text z-plain">59 | Picker::new(|s: &amp;String| AsRef::&lt;str&gt;::as_ref(s)); </span><span class="z-text z-plain"> | - - ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `&#39;1` must outlive `&#39;2` </span><span class="z-text z-plain"> | | | </span><span class="z-text z-plain"> | | return type of closure is &amp;&#39;2 str </span><span class="z-text z-plain"> | let&#39;s call the lifetime of this reference `&#39;1` </span></code></pre> <p>The problem is that the type signature of the closure <code>R</code> should also specify <code>S</code> outlives <code>'a</code>, but since <code>'a</code> is a parameter which is only local to <code>R</code>, we cannot capture it in the generic return type of <code>R</code>.</p> <p>In fact, this (invalid) type signature has a more fundamental issue: the type parameter <code>S</code> should not be constrained by <code>R</code>, but rather should be an <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/reference/items/associated-items.html#associated-types">associated type</a>. Just like the <code>for &lt;'a&gt;</code> lifetime bound, the type parameter <code>S</code> should only be visible within the renderer and we should not be bounding at the Picker implementation level. This is quite natural, since the return type of the closure should be decided by the specific implementation of the closure itself!</p> <p>Unfortunately, generic return types are currently<label for="in2">2</label><input type="checkbox" id="in2"><small>Well, at least as far as I am aware, and in Rust 1.82.</small> not supported in closures. The solution is to use a trait which imitates such a closure.</p> <h2 id="the-solution-closure-style-trait-with-generic-associated-types"><a href="#the-solution-closure-style-trait-with-generic-associated-types"></a>The solution: closure-style trait with generic associated types</h2> <p>First, let’s desugar the original closure type as an explicit trait:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-storage z-type z-trait z-rust">trait</span> <span class="z-entity z-name z-trait z-rust">Render</span>&lt;T&gt; <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>And then let’s add a <a rel="noopener" target="_blank" href="https://blog.rust-lang.org/2022/10/28/gats-stabilization.html">generic associated type</a> to allow the user to decide on a return value which may optionally borrow from the item itself.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-trait z-rust">trait</span> <span class="z-entity z-name z-trait z-rust">Render</span>&lt;T&gt; <span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Str</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-other z-rust">where</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-trait z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>The <code>where T: 'a</code> is a <a rel="noopener" target="_blank" href="https://blog.rust-lang.org/2022/10/28/gats-stabilization.html#non-local-requirements-for-where-clauses-on-gats">non-local requirement</a> since this bound is implied by the signature of render.</p> <p>Finally, we have the corresponding picker implementation:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-generic z-rust"><span class="z-entity z-name z-struct z-rust">Picker</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-meta z-struct z-rust"> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">renderer</span><span class="z-punctuation z-separator z-type z-rust">:</span> R, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">_marker</span><span class="z-punctuation z-separator z-type z-rust">:</span> <span class="z-meta z-generic z-rust">PhantomData<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust">Render<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">Picker</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>R <span class="z-keyword z-operator z-rust">as</span> <span class="z-meta z-generic z-rust">Render<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-path z-rust"><span class="z-punctuation z-accessor z-rust">::</span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-double-slash z-rust"><span class="z-punctuation z-definition z-comment z-rust">//</span> now we can internally render items for display </span></span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span>renderer<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">render</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>item</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>Note that the return type of <code>Picker::render</code> cannot actually be <code>&amp;'a str</code>, since it is possible that the <code>render</code> implementation could return a type which requires ownership (such as a <code>String</code>), and then calling <code>as_ref()</code> will return a reference to local variable which is immediately dropped.</p> <p>Now, for instance, if we want to render a value in terms of its <code>Display</code> implementation, we can implement a renderer for it:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-entity z-name z-struct z-rust">DisplayRenderer</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-support z-type z-rust">ToString</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-meta z-generic z-rust">Render<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">DisplayRenderer</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Str</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-support z-type z-rust">String</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-other z-rust">where</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> item<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">to_string</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>And in situations when the output might borrow from the item, this is permitted using the associated lifetime:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-entity z-name z-struct z-rust">PathRenderer</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-meta z-generic z-rust"><span class="z-support z-type z-rust">AsRef</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>Path<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-meta z-generic z-rust">Render<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">PathRenderer</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Str</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-other z-rust">where</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> item<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_ref</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">to_string_lossy</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>The main downside of this solution is that it is quite a bit more verbose than the closure syntax. In particular, we lose out on closure features such as automatic variable capturing.</p> <p>However, we still have a final trick: we can implement <code>Render&lt;T&gt;</code> for closure types!</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T, R<span class="z-punctuation z-separator z-rust">:</span> for <span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-begin z-rust">&gt;</span> <span class="z-support z-type z-rust">Fn</span><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T<span class="z-punctuation z-section z-group z-end z-rust">)</span> <span class="z-punctuation z-separator z-generic z-rust">-&gt;</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-meta z-generic z-rust">Render<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>T<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">R</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Str</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-meta z-generic z-rust">Cow<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span>, <span class="z-storage z-type z-rust">str</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-other z-rust">where</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> T<span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> T</span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-support z-function z-rust">self</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>item</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>Now whenever a <code>Render&lt;T&gt;</code> implementation is expected, the caller can also pass an appropriate closure.</p> <h3 id="comparison-with-other-solutions"><a href="#comparison-with-other-solutions"></a>Comparison with other solutions</h3> <p>To conclude, let’s briefly go over the previous concerns with the other solutions in relation to the implementation in this section. Some of the details in this section will go into some other implementation choices specific to <code>nucleo-picker</code>.</p> <h4 id="overhead-to-keep-track-of-items-1"><a href="#overhead-to-keep-track-of-items-1"></a>Overhead to keep track of items</h4> <p>Since this implementation is generic over the item type, we can take ownership of the item internally, keep track of the association between items and the match context, and then return (a reference to) the original item to the user when complete.</p> <h4 id="conversion-overhead"><a href="#conversion-overhead"></a>Conversion overhead</h4> <p>Since we are not exposing any types into which the user should convert their data, there is no additional development overhead.</p> <h4 id="extra-memory-overhead-with-lossy-representation-1"><a href="#extra-memory-overhead-with-lossy-representation-1"></a>Extra memory overhead with lossy representation</h4> <p>In principle, we still have memory overhead if we want an internal representation. In order to avoid the overhead from additionally storing the rendered representation, in <code>nucleo-picker</code>, we assume that the <code>Render</code> implementation is relatively efficient and simply call it again if the internal representation happens to be lossy (which is only the case in the presence of non-ASCII Unicode characters). This is reasonable since we only need to call <code>render</code> to generate the items which would actually be visible on the screen, which means that there is not too much pressure for optimized render performance.</p> <p>If render performance is exceptionally bad<label for="in3">3</label><input type="checkbox" id="in3"><small>A generous back-of-the-envelope calculation says, if we want 60 frames per second, and the terminal has 100 lines, then the render call should take on average 0.1 ms, which is an eternity from the perspective of your computer.</small>, unfortunately in this situation the library user will probably have to implement a custom wrapper type which internally caches the rendered representation.</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-generic z-rust"><span class="z-entity z-name z-struct z-rust">CachedItem</span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>D<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span></span><span class="z-meta z-struct z-rust"> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">data</span><span class="z-punctuation z-separator z-type z-rust">:</span> D, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-comment z-line z-documentation z-rust"><span class="z-punctuation z-definition z-comment z-rust">///</span> the pre-computed rendered version of `data` </span></span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">rendered</span><span class="z-punctuation z-separator z-type z-rust">:</span> String, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-modifier z-rust">pub</span> <span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-entity z-name z-struct z-rust">CachedItemRenderer</span></span><span class="z-punctuation z-terminator z-rust">;</span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span></span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>D<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-impl z-rust"> <span class="z-meta z-generic z-rust">Render<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-meta z-generic z-rust">CachedItem<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>D<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">CachedItemRenderer</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Str</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-keyword z-operator z-bitwise z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> <span class="z-storage z-type z-rust">str</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-other z-rust">where</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> D<span class="z-punctuation z-separator z-rust">:</span> <span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> <span class="z-meta z-generic z-rust">Item<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span>D<span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-keyword z-operator z-bitwise z-rust">&amp;</span>item<span class="z-punctuation z-accessor z-dot z-rust">.</span>rendered </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <p>However, since <code>Render</code> is only called a relatively low number of times per frame, in practice this should rarely be an issue.</p> <h4 id="implementation-on-foreign-types-1"><a href="#implementation-on-foreign-types-1"></a>Implementation on foreign types</h4> <p>Implementation on some foreign type <code>T</code> is now easy simply by implementing the relevant <code>Render&lt;T&gt;</code> implementation. Of course, there is still boilerplate required to deal with the foreign type. However, the key difference here is that the boilerplate is <em>restricted to the <code>Render</code> implementation</em>: when you implement a newtype wrapper, the boilerplate corrupts every downstream method call with <code>my_struct.0.method()</code> and introduces additional conversions to and from the relevant types. After you implement <code>Render&lt;T&gt;</code>, you can proceed to forget about any types you needed to introduce to handle rendering.</p> <h4 id="difficulty-in-default-implementation-1"><a href="#difficulty-in-default-implementation-1"></a>Difficulty in default implementation</h4> <p>Default implementation is simple just by defining a new <code>Render</code> trait, and does not require the user to wrap their own types in a wrapper. Changing the render behaviour does not require modifying <code>T</code>, and just needs a new <code>Render</code> implementation. Since <code>Render</code> is otherwise entirely independent, one could in principle even depend on <code>Render</code> implementations from entirely different crates.</p> <h4 id="inflexibility-with-stateful-rendering-patterns-1"><a href="#inflexibility-with-stateful-rendering-patterns-1"></a>Inflexibility with stateful rendering patterns</h4> <p>Since the <code>Render</code> implementation can internally contain (dynamic) state for rendering, stateful rendering patterns are simple to implement. The example from the earlier section would become:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-storage z-type z-struct z-rust">struct</span> </span><span class="z-meta z-struct z-rust"><span class="z-entity z-name z-struct z-rust">SubsetRenderer</span> </span><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"> <span class="z-variable z-other z-member z-rust">alllowed_fields</span><span class="z-punctuation z-separator z-type z-rust">:</span> <span class="z-meta z-generic z-rust">HashSet<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-support z-type z-rust">String</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span>, </span></span></span><span class="z-source z-rust"><span class="z-meta z-struct z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span><span class="z-source z-rust"> </span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-storage z-type z-impl z-rust">impl</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-generic z-rust">Render<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-meta z-generic z-rust">BTreeMap<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-support z-type z-rust">String</span>, <span class="z-support z-type z-rust">String</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span> <span class="z-keyword z-other z-rust">for</span></span><span class="z-meta z-impl z-rust"> <span class="z-entity z-name z-impl z-rust">SubsetRenderer</span> </span><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-storage z-type z-type z-rust">type</span> <span class="z-entity z-name z-type z-rust">Str</span><span class="z-keyword z-operator z-comparison z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-keyword z-operator z-comparison z-rust">&gt;</span> <span class="z-keyword z-operator z-assignment z-rust">=</span> <span class="z-support z-type z-rust">String</span><span class="z-punctuation z-terminator z-rust">;</span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"> <span class="z-meta z-function z-rust"><span class="z-meta z-function z-rust"><span class="z-storage z-type z-function z-rust">fn</span> </span><span class="z-entity z-name z-function z-rust">render</span></span><span class="z-meta z-generic z-rust"><span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">(</span><span class="z-keyword z-operator z-rust">&amp;</span><span class="z-variable z-parameter z-rust">self</span>, <span class="z-variable z-parameter z-rust">item</span><span class="z-punctuation z-separator z-rust">:</span> <span class="z-keyword z-operator z-rust">&amp;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span> <span class="z-meta z-generic z-rust">BTreeMap<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-support z-type z-rust">String</span>, <span class="z-support z-type z-rust">String</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span><span class="z-meta z-function z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-end z-rust">)</span></span></span></span><span class="z-meta z-function z-rust"> <span class="z-meta z-function z-return-type z-rust"><span class="z-punctuation z-separator z-rust">-&gt;</span> <span class="z-meta z-path z-rust"><span class="z-storage z-type z-rust"><span class="z-storage z-type z-rust">Self</span><span class="z-punctuation z-accessor z-rust">::</span></span></span><span class="z-meta z-generic z-rust">Str<span class="z-punctuation z-definition z-generic z-begin z-rust">&lt;</span><span class="z-storage z-modifier z-lifetime z-rust">&#39;a</span><span class="z-punctuation z-definition z-generic z-end z-rust">&gt;</span></span></span> </span><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-begin z-rust">{</span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> item<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">iter</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">filter</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">|</span></span></span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-variable z-parameter z-rust">k</span><span class="z-punctuation z-separator z-rust">,</span> _</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-section z-parameters z-end z-rust">|</span></span> </span><span class="z-meta z-function z-closure z-rust"><span class="z-variable z-language z-rust">self</span><span class="z-punctuation z-accessor z-dot z-rust">.</span>allowed_fields<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">contains</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span>k<span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">as_str</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">map</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-punctuation z-section z-parameters z-begin z-rust">|</span></span></span><span class="z-meta z-function z-closure z-rust"><span class="z-meta z-function z-parameters z-rust"><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust">_<span class="z-punctuation z-separator z-rust">,</span> <span class="z-variable z-parameter z-rust">v</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span><span class="z-punctuation z-section z-parameters z-end z-rust">|</span></span> </span><span class="z-meta z-function z-closure z-rust">v</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">join</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span><span class="z-string z-quoted z-double z-rust"><span class="z-punctuation z-definition z-string z-begin z-rust">&quot;</span>, <span class="z-punctuation z-definition z-string z-end z-rust">&quot;</span></span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> <span class="z-punctuation z-accessor z-dot z-rust">.</span><span class="z-support z-function z-rust">into</span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">(</span></span><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">)</span></span> </span></span></span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"><span class="z-meta z-function z-rust"><span class="z-meta z-block z-rust"> </span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></span></span><span class="z-source z-rust"><span class="z-meta z-impl z-rust"><span class="z-meta z-block z-rust"></span><span class="z-meta z-block z-rust"><span class="z-punctuation z-section z-block z-end z-rust">}</span></span></span> </span></code></pre> <h4 id="inflexible-return-type-1"><a href="#inflexible-return-type-1"></a>Inflexible return type</h4> <p>The return type is fully flexible and can be chosen by the user depending on the requirements for rendering the specific type. The lifetime in the associated type ensures that the rendered value can borrow from the item, if such an optimization is relevant to the <code>Render</code> implementation.</p> Against Open Workspaces 2023-01-06T00:00:00+00:00 2023-01-06T00:00:00+00:00 https://rutar.org/writing/against-open-workspaces/ <h2 id="context"><a href="#context"></a>Context</h2> <p>It was recently pointed out to me that part of the initial proposal concerning the construction of a new mathematics building at the University of St Andrews includes the creation of an open hot-desk workspace for postgraduate research students. While functionally and bureaucratically appealing, I believe that this form of workspace organization is fundamentally opposed to the requirements of postgraduate research students.</p> <p>Open workspaces are a common workspace “solution”, popularly depicted in media through association with certain US tech companies and other modern institutions. Such workspaces typically contain some or all of the following features:</p> <ol> <li>Large, open spaces with individual desks with minimal physical and visual obstruction between desks.</li> <li>Non-permanent desk space (<em>hot-desking</em>), typically combined with an alternative method for storage of belongings, such as lockers.</li> <li>Institutionally-managed communal resources.</li> </ol> <p>Note that open workspaces are already in use in universities in Scotland: the most notable example I am familiar with is the <a rel="noopener" target="_blank" href="https://www.ed.ac.uk/bayes">Bayes Centre</a> at the University of Edinburgh. My objection to this proposal, especially as it concerns usage by postgraduate students, is broken down into four parts:</p> <ol> <li><a href="https://rutar.org/writing/against-open-workspaces/#identity-and-ownership-of-space">Identity and ownership of space</a></li> <li><a href="https://rutar.org/writing/against-open-workspaces/#environmental-familiarity">Environmental familiarity</a></li> <li><a href="https://rutar.org/writing/against-open-workspaces/#impact-on-productivity-and-collaboration">Impact on productivity and collaboration</a></li> <li><a href="https://rutar.org/writing/against-open-workspaces/#social-conditions">Social conditions</a></li> </ol> <p>Let’s discuss these parts in detail.</p> <h2 id="objections-to-open-workspaces"><a href="#objections-to-open-workspaces"></a>Objections to open workspaces</h2> <h3 id="identity-and-ownership-of-space"><a href="#identity-and-ownership-of-space"></a>Identity and ownership of space</h3> <p>A permanent desk in a semi-private workspace is much more than a space to do work: it is a physical embodiment of one’s affiliation with and membership in the university itself. Embedded within the provisioning of the permanent desk is the commitment by the university that you are one of it’s important <em>individual</em> members. This sense of membership and community provides an important source of social cohesion.</p> <p>This notion of ownership can also transcend the individual members. Here, perhaps, an example is useful. As a mathematics postgraduate student at the University of St Andrews in the <a rel="noopener" target="_blank" href="https://maths-analysis.wp.st-andrews.ac.uk/">analysis research group</a>, I currently have a permanent desk in the so-called <em>analysis bunker</em>. This office has been used by analysis research students continuously over the past 10 years, and this history is present through the various idiosyncrasies of the space itself: copies of old books and publications, the name-tag-chain of past members of the office, the dilapidated giraffe poster on the wall.</p> <p>I perceive our shared postgraduate office as more than just a space in which to work and interact with others: the space helps to constitute my membership of the analysis group itself. However, a personal, permanent, and physical connection to the space is a core requirement for such a space to develop and maintain its individual identity. The creation of a shared workspace is more than just the destruction of such spaces: it is the creation of an environment which implicitly precludes the creation of such spaces entirely.</p> <h3 id="environmental-familiarity"><a href="#environmental-familiarity"></a>Environmental familiarity</h3> <p>One of the key comforts of the personal home is its <em>familiarity</em>. This familiarity has at least two characteristic features: social repetition (you are exposed to and interact with the same small group of people) and physical persistence (the functional properties of your surroundings is unchanged over time).</p> <p>Perhaps you are sitting beside a new, somewhat familiar but different person with different tastes and preferences for their workspace interactions. Perhaps your keyboard functions in a slightly different way than the keyboard you used last time (the <kbd><code>A</code></kbd> key is sticky, rather than the <kbd><code>S</code></kbd> key). Such micro-inconsistencies obstruct the effective usage of space. In other words, it is important that the objects of daily use are not only <em>similar</em>, but that they are <em>identical</em>. Moreover, while the magnitude of these irritations is small, the scale is substantially larger: you experience this friction <em>every single time you use the space</em>.</p> <p>The usage of open workspaces, and particularly an impermanent solution in which one does not even have access to a fixed desk, is diametrically opposed to comfortable usage.</p> <h3 id="impact-on-productivity-and-collaboration"><a href="#impact-on-productivity-and-collaboration"></a>Impact on productivity and collaboration</h3> <p>Open workspaces are often detrimental to the focused work essential to academic research. Constant background sounds, movement, and the presence of others (both through distracting social interaction and simply the lack of privacy) are detrimental to focused work. Moreover, if one does not have a fixed desk, one must repeatedly set up the workspace each day, and must deal with the friction of a new environment as highlighted earlier.</p> <p>However, one might argue that while there is a loss in productivity, the value is outweighed by the corresponding social benefits of an open workspace. In fact, the initial proposal explicitly mentions the following goal of the University of St Andrews: to “promote working across disciplines and interdisciplinarity in our priority thematic areas”. However, open spaces often have the inverse impact.</p> <p>Bernstein and Turban explore this dynamic in <a rel="noopener" target="_blank" href="https://royalsocietypublishing.org/doi/epdf/10.1098/rstb.2017.0239">their study on interaction in open workspaces</a>. In their particular experimental environment, they observe that an open workspace actually <em>decreases</em> interactions between the members, and by a substantial amount (approximately 70%). In the introduction, they observe that “spatial boundaries have long served a functional role at multiple levels of analysis, helping people make sense of their environment by modularizing it, clarifying who is watching and who is not, who has information and who does not, who belongs and who does not, who controls what and who does not, to whom one answers and to whom one does not.” This implies a possible conceptual explanation for the reduction of interaction: since one does not have control over one’s surroundings, one must constantly accommodate for ones <em>social appearance</em>. For instance, one might be more inclined to give the appearance of continual focused work, even when the most beneficial action would be to take a break and socialize (or read a book).</p> <p>Moreover, even if such a space did foster collaboration, it is unclear that the form of collaboration is relevant for postgraduate research students. For a postgraduate researcher who does not have wide expertise in a field, it is very difficult to collaborate with people in vastly different areas. In an open workspace, the interactions among postgraduate students will primarily be among students with different mathematical expertise; for the purposes of collaborative work, such interactions are essentially useless. Of course, this is not to deny the importance of social interaction among postgraduate students: but the solution is not to place all postgraduate students in a large open workspace.</p> <p>In this sense, small shared offices are a better method to promote collaboration. Through the familiarity of the smaller group, one can become comfortable with the social environment. Moreover, if some thought is put into the membership of the offices, the interactions will be substantially more valuable in a collaborative sense.</p> <h3 id="social-conditions"><a href="#social-conditions"></a>Social conditions</h3> <p>I believe that access to permanent physical space is in fact <em>more important</em> for postgraduate research students. Two (typical) socio-economic features of postgraduate research students are that they have lower income, and that their housing situation is impermanent and requires semi-frequent moving. Both of these features are opposed to the creation of a comfortable workspace at home. A permanent, semi-private workspace in the university, which persists for the duration of the research degree, provides a stable environment on which one can fall back.</p> <p>An explicit example is given by the current (i.e. Winter 2023) energy cost crisis in the UK: since the costs of heating have risen so much, it is cheaper—and more comfortable—to use university spaces when the weather is cold. Moreover, this effect is amplified when the cost of energy is a proportionally larger fraction of total income. Since an open workspace does not provide the familiarity and comfort that one might hope to find at home, there is no reasonable fallback in this situation.</p> <h2 id="some-final-thoughts-on-community"><a href="#some-final-thoughts-on-community"></a>Some final thoughts on community</h2> <p>From an institutional viewpoint, open workspaces are incredibly tempting: they are cost-effective through their economies of scale gained by abstracting the physical and spatial requirements of an individual and meeting them in a systematic way. However, it is precisely this abstraction of individual members—the viewing of postgraduate students as a group <em>defined by</em> their requirements and outputs—which dissolves the individual. In this sense, I hope that my criticism of open workspaces is understood as more than simply an opposition to certain architectural choices: it is an appeal to cherish the <em>individuals</em> who comprise the community which is our university. The maintenance and perpetuation of this institutional mode is challenging, since the abstractive success of a bureaucratic system is achieved <em>through</em> this process of individual dissolution.</p> <p>It is as a passionate and proud member of my current educational institution, and it is through my responsibility not just to myself but also the future members of this university, that I oppose these fracturing structural forces and commit to the preservation of this wonderful community.</p> Oath Dice and Combat Odds 2022-04-03T00:00:00+00:00 2022-04-03T00:00:00+00:00 https://rutar.org/writing/oath-dice-and-combat-odds/ <h2 id="the-oath-combat-system"><a href="#the-oath-combat-system"></a>The Oath combat system</h2> <p><a rel="noopener" target="_blank" href="https://ledergames.com/products/oath-chronicles-of-empire-exile">Oath</a> is a great board game, which is in some sense a game about <em>history</em>, and in others a game about <em>empires</em>. If you’re not already familiar with the game, I’d highly recommend that you give it a try.</p> <p>In this article, I will discuss some of the odds in the oath combat system. I will be relatively light on mathematical details: for a full derivation of the formulas, you can see <a href="https://rutar.org/writing/oath-dice-and-combat-odds/oath_dice_odds.pdf">my writeup</a>.</p> <p>In the <a href="https://rutar.org/writing/oath-dice-and-combat-odds/#combat-heuristics">combat heuristics</a> section, I will discuss some good rules for estimating your odds in-game.<label for="in1">1</label><input type="checkbox" id="in1"><small>Thank you to users <a rel="noopener" target="_blank" href="https://boardgamegeek.com/user/steveowen">@steveowen</a> and <a rel="noopener" target="_blank" href="https://boardgamegeek.com/user/Samuel%20Vriezen">@Samuel Vriezen</a> for comments on BGG which motivated this section.</small> In particular, the <a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-loss-estimate">unit loss estimate</a> section gives a quick procedure you can follow to work out how many units you would expect to have left, after combat.</p> <p>If you instead want to see some pre-computed unit loss tables, here are the links:</p> <ul> <li><a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-difference-percentiles">Unit difference</a></li> <li><a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-loss-with-3-defenders">Unit loss with 3 defenders</a></li> <li><a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-loss-with-5-defenders">Unit loss with 5 defenders</a></li> <li><a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-loss-with-8-defenders">Unit loss with 8 defenders</a></li> </ul> <p>If you use <a rel="noopener" target="_blank" href="https://www.wolfram.com/mathematica/">Mathematica</a>, I’ve also implemented all the distributions in <a href="oath.nb" download>this Mathematica notebook</a>.</p> <p>With that out of the way, let’s see some more detail!</p> <h3 id="combat-heuristics"><a href="#combat-heuristics"></a>Combat heuristics</h3> <p>The main insight from the odds are the zero-loss curves. Looking at the <a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-difference-percentiles">unit difference tables</a>, we can see the various defence dice / attack dice pairings which result in the attacker not having to sacrifice any units 50% of the time:</p> <ul> <li><var>1</var> vs. <var>2</var></li> <li><var>2</var> vs. <var>3</var></li> <li><var>3</var> vs. <var>4</var></li> <li><var>4</var> vs. <var>6</var></li> <li><var>5</var> vs. <var>7</var></li> <li><var>6</var> vs. <var>9</var></li> <li><var>7</var> vs. <var>12</var></li> <li><var>8</var> vs. <var>15</var></li> <li>etc.</li> </ul> <p>Fitting a quadratic to this curve and rounding slightly, we can estimate that, when there are <var>n</var> defending dice, you should have <var>2+(n<sup>2</sup>-n)/4</var> attacking units in order to beat the defending dice roll 50% of the time. Now if you have a different number of attacking units, since there are 3 attacking outcomes worth 0.5, and 3 attacking outcomes worth either 1 or 2 (with unit sacrifice), for every extra attacking dice you have, you expect to get an extra 0.75 attacking power in your roll.</p> <h3 id="unit-loss-estimate"><a href="#unit-loss-estimate"></a>Unit loss estimate</h3> <p>Combining these observations gives the following procedure to estimate the number of attacking units you will have remaining after combat:</p> <ol> <li>Determine the number of defending dice, <var>n</var>, and compute the <strong>attack balance point</strong> <var>k=2+(n<sup>2</sup>-n)/4</var> <em>(for example, if there are 8 defending dice, the attack balance point is 16)</em></li> <li>Determine your attacking dice count, <var>a</var>, and compute the <strong>power adjustment</strong> <var>1+(k-a)·3/4</var> <em>(for example, if you have 12 attacking dice, the power adjustment is 4)</em></li> <li>Add the defending unit count and add the power adjustment <em>(for example, if there are 8 defending units, the final result is 12)</em></li> </ol> <p>Now, 50% of the time, you should expect to lose at most 12 units. Let’s compare this to the table computing the <a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-loss-with-8-defenders">unit loss with 8 defenders</a>: this table states that for 8 defending dice, 8 defenders, and 13 attacking dice, you would lose 11 units 50% of the time. This estimate isn’t perfect, but it is relatively easy to compute in-game!</p> <p>If you’re in a hurry and don’t want to do too much mental math, simply doing (1) will give you a good idea for the number of units you’d want to not have to sacrifice any units 50% of the time. If you want, you can also just memorize the zero-loss curve table to get a more precise estimate.</p> <p>If you instead want the estimate with ≈80% certainty (instead of 50%), you can perform the same estimate by computing the <strong>attack balance point</strong> with <var>5 - 3n/4 + n<sup>2</sup>/2</var>, rounded down. And for ≈90% certainty, use <var>5 - 0.9 n + 0.6 n<sup>2</sup></var>, rounded down. This number gets very large, very fast!</p> <p>Also, if you have a card which means you do not sacrifice units when you roll skulls, simply adjust the <var>3/4</var> factor to <var>11/12</var>.</p> <h2 id="precise-combat-odds"><a href="#precise-combat-odds"></a>Precise combat odds</h2> <h3 id="combat-mechanics"><a href="#combat-mechanics"></a>Combat mechanics</h3> <p>Combat is an important part of Oath, and the combat system is relatively straightforward, while also being feature-rich with somewhat deceptive odds. Cole Wehrle (the designer of Oath) has written about the <a rel="noopener" target="_blank" href="https://boardgamegeek.com/thread/2473354/designer-diary-18-campaign-revisited">development process</a> of the combat system.</p> <p>During combat, there are two sets of dice: attacking, and defending. The sides of the dice are shown in red and blue respectively in the image below.</p> <figure> <img src="oath_dice_faces.jpg" alt="Faces of the oath attack and defence dice."> <figcaption>Oath attack and defence dice faces. © Leder Games</figcaption> </figure> The attacker has a power of the number of solid swords roll, along with half of the outlined swords (rounded down), and must sacrifice 1 unit for each skull. <p>The defending distribution is given by taking multiplying the number of shields by 2 for every ×2 dice roll.</p> <h3 id="computing-the-distributions"><a href="#computing-the-distributions"></a>Computing the distributions</h3> <p>The full details for the probability computations can be found in <a href="https://rutar.org/writing/oath-dice-and-combat-odds/oath_dice_odds.pdf">this writeup</a>.</p> <p>The general idea as as follows. A convenient strategy to compute probabilities involving binomial expansions is to use <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Generating_function">generating functions</a>. While this particular application does not require any sophisticated theory, the approach is conceptually nice.</p> <p>For example, let’s compute the attack odds by tracking the number of skulls and swords rolled. Consider the polynomial <var>(3x<sup>½</sup>+2x+x<sup>2</sup>y)<sup>n</sup></var>, where the coefficients are chosen as such since there are <var>3</var> dice with a hollow sword, 2 dice with a solid sword, and 1 dice with 2 solid swords (contributing the <var>x<sup>2</sup></var>) and 1 skull (contributing the <var>y</var>). We count the hollow swords as ½, which we can round later.</p> <p>Then, extracting the coefficient of the term <var>x<sup>z</sup>y<sup>k</sup></var> gives the number of ways to roll exactly <var>z</var> attack points (without rounding) with <var>k</var> skulls. Why does this work? When we compute the power of the polynomial raised to the <var>n</var><sup>th</sup> power, the resulting polynomial (without collecting terms) contains all possible choices of <var>n</var> dice rolls,</p> <p>Finally, we need to do some rounding: conveniently, it is alright to round the total, rather than the number of hollow sword rolls, since they are equivalent.</p> <p>For the defence odds, suppose we are rolling <var>n</var> dice where <var>n=2<sup>m</sup>·l</var> with <var>l</var> odd. Then the only way to roll exactly <var>n</var> is to have <var>k</var> dice show ×2 (for some <var>k</var> between <var>0</var> and <var>m</var>), and then roll <var>2<sup>m-k</sup>·l</var> using only shields with the remaining dice. The number of ways to roll <var>k</var> ×2 dice is <var>n</var> choose <var>k</var>, and the number of ways to roll <var>2<sup>m-k</sup>·l</var> using only shields is the coefficient of <var>x<sup>2<sup>m-k</sup>·l</sup></var> in the polynomial <var>(2+2x+x<sup>2</sup>)<sup>n-k</sup></var>.</p> <h2 id="summary-of-odds"><a href="#summary-of-odds"></a>Summary of odds</h2> <p>In each entry, the rows correspond to the number of attacking dice, and the columns correspond to the number of defending dice. The three numbers are in each entry are the 50<sup>th</sup>, 90<sup>th</sup>, and 99<sup>th</sup> percentiles, respectively.</p> <p>In order to use the <a href="https://rutar.org/writing/oath-dice-and-combat-odds/#unit-difference-percentiles">unit difference table</a>, choose the row with the number of attacking dice and the column with the number of defending dice. Then, for example, the second entry is is the number of units you will need to sacrifice 90% of the time in order to (strictly) overcome the defence roll power.</p> <p>Other factors, such as the number of skulls rolled, as well as the defensive unit power, will influence the outcome as well. In order to estimate how many units you will lose, you need to</p> <ol> <li>Look up the corresponding value in the table for your attack and defence dice counts, as well as your desired percentile.</li> <li>Add a number equal to ⅙ of your dice roll (this is the average loss from rolling skulls).</li> <li>Add the defensive unit power.</li> </ol> <p>The resulting number is approximately a lower bound on the number of units you will have left over after the combat. Note this does not account for the case where you roll well beyond the defensive strength, but still need to sacrifice units because of skulls. To get an idea of what these numbers look like for certain defensive unit counts, I’ve included 3 additional tables at the end of this article.</p> <p>Certain cards, such as <em>Rangers</em> and <em>Kindred Warriors</em>, also allow the attacker to roll without sacrificing units from double sword dice rolls.</p> <figure> <img src="rangers_oath_card.webp" alt="Rangers Oath card"> <img src="kindred_warriors_oath_card.webp" alt="Kindred Warriors Oath card"> <figcaption>Some cards which allow ignoring skulls. © Leder Games</figcaption> </figure> <p>Since there are many possible combinations, I recommend using the <code>combatSummary</code> function in the <a href="oath.nb" download>Mathematica notebook</a>, which will report the cumulative distribution of the number of attacking units that survive.<label for="in2">2</label><input type="checkbox" id="in2"><small>Eventually, I may write an online Javascript tool to do this computation.</small></p> <h3 id="unit-difference-percentiles"><a href="#unit-difference-percentiles"></a>Unit difference percentiles</h3> <p>This is the distribution of the defence roll minus the attack roll. Negative numbers favour the attacker, and positive numbers favour the defender.</p> <table class="large"><thead><tr><th></th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th><th>9</th><th>10</th><th>11</th><th>12</th></tr></thead><tbody><tr><th>1</th><td>1, 2, 3</td><td>2, 4, 5</td><td>3, 5, 9</td><td>4, 8, 13</td><td>6, 12, 21</td><td>7, 17, 33</td><td>9, 24, 48</td><td>12, 32, 65</td><td>15, 41, 97</td><td>19, 56, 129</td><td>24, 71, 192</td><td>29, 89, 256</td></tr><tr><th>2</th><td>0, 1, 2</td><td>1, 3, 4</td><td>2, 4, 8</td><td>3, 8, 12</td><td>5, 11, 20</td><td>6, 16, 32</td><td>8, 23, 47</td><td>12, 31, 64</td><td>15, 40, 96</td><td>18, 55, 128</td><td>23, 70, 191</td><td>28, 88, 255</td></tr><tr><th>3</th><td>-1, 1, 2</td><td>0, 2, 4</td><td>1, 4, 7</td><td>2, 7, 12</td><td>4, 10, 19</td><td>6, 15, 31</td><td>8, 22, 46</td><td>11, 30, 63</td><td>14, 39, 95</td><td>17, 54, 127</td><td>22, 69, 190</td><td>27, 87, 254</td></tr><tr><th>4</th><td>-2, 0, 1</td><td>-1, 1, 3</td><td>0, 3, 6</td><td>1, 6, 11</td><td>3, 9, 19</td><td>5, 14, 30</td><td>7, 21, 45</td><td>10, 29, 62</td><td>13, 38, 94</td><td>16, 53, 126</td><td>21, 68, 189</td><td>26, 87, 253</td></tr><tr><th>5</th><td>-3, -1, 0</td><td>-2, 0, 2</td><td>-1, 2, 5</td><td>1, 5, 10</td><td>2, 8, 18</td><td>4, 13, 29</td><td>6, 20, 45</td><td>9, 28, 61</td><td>12, 37, 93</td><td>15, 52, 125</td><td>20, 67, 188</td><td>25, 86, 252</td></tr><tr><th>6</th><td>-4, -2, 0</td><td>-3, 0, 1</td><td>-2, 1, 4</td><td>0, 4, 9</td><td>1, 7, 17</td><td>3, 12, 28</td><td>5, 19, 44</td><td>8, 27, 61</td><td>11, 36, 92</td><td>15, 51, 124</td><td>19, 65, 187</td><td>25, 85, 251</td></tr><tr><th>7</th><td>-4, -2, -1</td><td>-4, -1, 1</td><td>-3, 0, 4</td><td>-1, 3, 8</td><td>0, 7, 16</td><td>2, 11, 27</td><td>4, 18, 43</td><td>7, 26, 60</td><td>10, 35, 91</td><td>14, 50, 123</td><td>18, 64, 186</td><td>24, 84, 250</td></tr><tr><th>8</th><td>-5, -3, -2</td><td>-4, -2, 0</td><td>-3, 0, 3</td><td>-2, 2, 8</td><td>-1, 6, 15</td><td>1, 10, 26</td><td>3, 17, 42</td><td>6, 25, 59</td><td>9, 34, 90</td><td>13, 49, 123</td><td>17, 63, 185</td><td>23, 84, 249</td></tr><tr><th>9</th><td>-6, -4, -3</td><td>-5, -3, -1</td><td>-4, -1, 2</td><td>-3, 1, 7</td><td>-2, 5, 14</td><td>0, 9, 25</td><td>2, 16, 41</td><td>5, 24, 58</td><td>8, 33, 89</td><td>12, 48, 122</td><td>16, 62, 184</td><td>22, 83, 248</td></tr><tr><th>10</th><td>-7, -5, -3</td><td>-6, -4, -2</td><td>-5, -2, 1</td><td>-4, 0, 6</td><td>-2, 4, 13</td><td>-1, 8, 24</td><td>1, 15, 40</td><td>4, 23, 57</td><td>7, 33, 88</td><td>11, 47, 121</td><td>15, 61, 183</td><td>21, 82, 247</td></tr><tr><th>11</th><td>-8, -6, -4</td><td>-7, -5, -3</td><td>-6, -3, 0</td><td>-5, 0, 5</td><td>-3, 3, 12</td><td>-2, 8, 23</td><td>1, 14, 39</td><td>3, 22, 56</td><td>6, 32, 87</td><td>10, 46, 120</td><td>14, 60, 182</td><td>20, 81, 246</td></tr><tr><th>12</th><td>-9, -7, -5</td><td>-8, -5, -3</td><td>-7, -4, -1</td><td>-6, -1, 4</td><td>-4, 2, 11</td><td>-2, 7, 22</td><td>0, 13, 38</td><td>2, 21, 55</td><td>5, 31, 86</td><td>9, 45, 119</td><td>14, 59, 181</td><td>19, 80, 245</td></tr><tr><th>13</th><td>-10, -7, -6</td><td>-9, -6, -4</td><td>-8, -5, -1</td><td>-7, -2, 3</td><td>-5, 1, 11</td><td>-3, 6, 22</td><td>-1, 12, 37</td><td>1, 20, 55</td><td>5, 30, 86</td><td>8, 44, 118</td><td>13, 58, 180</td><td>18, 79, 244</td></tr><tr><th>14</th><td>-11, -8, -6</td><td>-10, -7, -5</td><td>-9, -5, -2</td><td>-8, -3, 2</td><td>-6, 0, 10</td><td>-4, 5, 21</td><td>-2, 11, 36</td><td>1, 19, 54</td><td>4, 29, 85</td><td>7, 43, 117</td><td>12, 57, 179</td><td>17, 78, 243</td></tr><tr><th>15</th><td>-12, -9, -7</td><td>-11, -8, -6</td><td>-10, -6, -3</td><td>-8, -4, 2</td><td>-7, -1, 9</td><td>-5, 4, 20</td><td>-3, 10, 35</td><td>0, 18, 53</td><td>3, 28, 84</td><td>6, 42, 116</td><td>11, 56, 178</td><td>16, 78, 242</td></tr><tr><th>16</th><td>-13, -10, -8</td><td>-12, -9, -6</td><td>-11, -7, -4</td><td>-9, -5, 1</td><td>-8, -1, 8</td><td>-6, 3, 19</td><td>-4, 9, 34</td><td>-1, 17, 52</td><td>2, 27, 83</td><td>5, 41, 115</td><td>10, 55, 177</td><td>15, 77, 241</td></tr><tr><th>17</th><td>-14, -11, -9</td><td>-13, -10, -7</td><td>-12, -8, -5</td><td>-10, -6, 0</td><td>-9, -2, 7</td><td>-7, 2, 18</td><td>-5, 8, 33</td><td>-2, 16, 51</td><td>1, 26, 82</td><td>5, 40, 115</td><td>9, 54, 176</td><td>15, 76, 240</td></tr><tr><th>18</th><td>-14, -12, -9</td><td>-14, -10, -8</td><td>-13, -9, -6</td><td>-11, -7, -1</td><td>-10, -3, 6</td><td>-8, 1, 17</td><td>-6, 8, 32</td><td>-3, 15, 50</td><td>0, 25, 81</td><td>4, 39, 114</td><td>8, 53, 175</td><td>14, 75, 239</td></tr><tr><th>19</th><td>-15, -12, -10</td><td>-15, -11, -9</td><td>-13, -10, -6</td><td>-12, -7, -2</td><td>-11, -4, 5</td><td>-9, 0, 16</td><td>-7, 7, 31</td><td>-4, 15, 49</td><td>-1, 24, 80</td><td>3, 38, 113</td><td>7, 53, 174</td><td>13, 74, 238</td></tr><tr><th>20</th><td>-16, -13, -11</td><td>-15, -12, -10</td><td>-14, -11, -7</td><td>-13, -8, -3</td><td>-11, -5, 4</td><td>-10, 0, 15</td><td>-8, 6, 30</td><td>-5, 14, 48</td><td>-2, 24, 79</td><td>2, 37, 112</td><td>6, 52, 173</td><td>12, 73, 237</td></tr><tr><th>21</th><td>-17, -14, -12</td><td>-16, -13, -10</td><td>-15, -11, -8</td><td>-14, -9, -4</td><td>-12, -6, 3</td><td>-11, -1, 14</td><td>-8, 5, 30</td><td>-6, 13, 47</td><td>-3, 23, 78</td><td>1, 36, 111</td><td>5, 51, 172</td><td>11, 72, 236</td></tr><tr><th>22</th><td>-18, -15, -13</td><td>-17, -14, -11</td><td>-16, -12, -9</td><td>-15, -10, -4</td><td>-13, -7, 2</td><td>-11, -2, 13</td><td>-9, 4, 29</td><td>-7, 12, 47</td><td>-4, 22, 77</td><td>0, 35, 110</td><td>4, 50, 171</td><td>10, 71, 235</td></tr><tr><th>23</th><td>-19, -16, -13</td><td>-18, -15, -12</td><td>-17, -13, -10</td><td>-16, -11, -5</td><td>-14, -8, 2</td><td>-12, -3, 12</td><td>-10, 3, 28</td><td>-8, 11, 46</td><td>-5, 21, 76</td><td>-1, 34, 109</td><td>4, 49, 170</td><td>9, 70, 234</td></tr><tr><th>24</th><td>-20, -17, -14</td><td>-19, -16, -13</td><td>-18, -14, -11</td><td>-17, -12, -6</td><td>-15, -9, 1</td><td>-13, -4, 12</td><td>-11, 2, 27</td><td>-9, 10, 45</td><td>-5, 20, 75</td><td>-2, 33, 108</td><td>3, 48, 169</td><td>8, 70, 234</td></tr><tr><th>25</th><td>-21, -18, -15</td><td>-20, -16, -14</td><td>-19, -15, -11</td><td>-18, -13, -7</td><td>-16, -9, 0</td><td>-14, -5, 11</td><td>-12, 1, 26</td><td>-9, 9, 44</td><td>-6, 19, 75</td><td>-3, 32, 107</td><td>2, 47, 168</td><td>7, 69, 233</td></tr></tbody></table> <h3 id="unit-loss-with-3-defenders"><a href="#unit-loss-with-3-defenders"></a>Unit loss with 3 defenders</h3> <p>This is a table of the attacking unit loss given that there are 3 defenders.</p> <table class="large"><thead><tr><th></th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th><th>9</th><th>10</th><th>11</th><th>12</th></tr></thead><tbody><tr><th>1</th><td>4, 5, 6</td><td>5, 7, 8</td><td>6, 9, 12</td><td>7, 11, 16</td><td>9, 15, 24</td><td>11, 20, 36</td><td>13, 27, 51</td><td>15, 35, 68</td><td>19, 44, 100</td><td>23, 59, 132</td><td>27, 75, 195</td><td>32, 92, 259</td></tr><tr><th>2</th><td>3, 5, 5</td><td>4, 6, 7</td><td>5, 8, 11</td><td>7, 11, 15</td><td>8, 15, 23</td><td>10, 19, 35</td><td>12, 26, 51</td><td>15, 34, 67</td><td>18, 43, 99</td><td>22, 58, 131</td><td>26, 74, 194</td><td>31, 91, 258</td></tr><tr><th>3</th><td>3, 4, 5</td><td>4, 5, 7</td><td>5, 7, 10</td><td>6, 10, 15</td><td>7, 14, 22</td><td>9, 18, 34</td><td>11, 26, 50</td><td>14, 34, 66</td><td>18, 42, 98</td><td>21, 58, 130</td><td>26, 73, 194</td><td>30, 90, 258</td></tr><tr><th>4</th><td>2, 3, 4</td><td>3, 5, 6</td><td>4, 6, 9</td><td>5, 9, 14</td><td>7, 13, 22</td><td>9, 17, 33</td><td>10, 25, 49</td><td>13, 33, 66</td><td>17, 41, 97</td><td>20, 57, 129</td><td>25, 72, 193</td><td>30, 90, 257</td></tr><tr><th>5</th><td>1, 3, 3</td><td>2, 4, 5</td><td>3, 6, 9</td><td>4, 8, 13</td><td>6, 12, 21</td><td>8, 17, 33</td><td>10, 24, 48</td><td>12, 32, 65</td><td>16, 41, 97</td><td>19, 56, 129</td><td>24, 72, 192</td><td>29, 89, 256</td></tr><tr><th>6</th><td>1, 2, 3</td><td>2, 3, 4</td><td>3, 5, 8</td><td>4, 8, 13</td><td>5, 11, 20</td><td>7, 16, 32</td><td>9, 23, 48</td><td>12, 31, 64</td><td>15, 40, 96</td><td>19, 55, 128</td><td>23, 71, 191</td><td>28, 88, 255</td></tr><tr><th>7</th><td>1, 2, 4</td><td>2, 3, 4</td><td>2, 4, 7</td><td>3, 7, 12</td><td>4, 11, 20</td><td>6, 15, 31</td><td>8, 22, 47</td><td>11, 30, 63</td><td>14, 39, 95</td><td>18, 54, 127</td><td>23, 70, 190</td><td>27, 88, 255</td></tr><tr><th>8</th><td>1, 3, 4</td><td>1, 3, 4</td><td>2, 4, 7</td><td>3, 6, 11</td><td>4, 10, 19</td><td>5, 14, 30</td><td>7, 22, 46</td><td>10, 30, 63</td><td>14, 38, 94</td><td>17, 54, 127</td><td>22, 69, 190</td><td>27, 87, 254</td></tr><tr><th>9</th><td>1, 3, 4</td><td>1, 3, 4</td><td>2, 4, 6</td><td>2, 5, 10</td><td>3, 9, 18</td><td>5, 14, 30</td><td>7, 21, 45</td><td>9, 29, 62</td><td>13, 38, 94</td><td>16, 53, 126</td><td>21, 68, 189</td><td>26, 86, 253</td></tr><tr><th>10</th><td>2, 3, 5</td><td>2, 3, 5</td><td>2, 4, 5</td><td>2, 5, 10</td><td>3, 8, 17</td><td>4, 13, 29</td><td>6, 20, 45</td><td>9, 28, 61</td><td>12, 37, 93</td><td>16, 52, 125</td><td>20, 68, 188</td><td>25, 86, 252</td></tr><tr><th>11</th><td>2, 3, 5</td><td>2, 3, 5</td><td>2, 4, 5</td><td>2, 4, 9</td><td>3, 8, 17</td><td>4, 12, 28</td><td>5, 19, 44</td><td>8, 27, 60</td><td>11, 36, 92</td><td>15, 51, 124</td><td>19, 67, 187</td><td>25, 85, 251</td></tr><tr><th>12</th><td>2, 4, 5</td><td>2, 4, 5</td><td>2, 4, 5</td><td>2, 4, 8</td><td>3, 7, 16</td><td>3, 11, 27</td><td>5, 18, 43</td><td>7, 27, 60</td><td>11, 35, 91</td><td>14, 51, 124</td><td>19, 66, 186</td><td>24, 84, 251</td></tr><tr><th>13</th><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 8</td><td>3, 6, 15</td><td>3, 11, 27</td><td>4, 18, 42</td><td>7, 26, 59</td><td>10, 35, 91</td><td>13, 50, 123</td><td>18, 65, 186</td><td>23, 83, 250</td></tr><tr><th>14</th><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 7</td><td>3, 6, 14</td><td>3, 10, 26</td><td>4, 17, 42</td><td>6, 25, 58</td><td>9, 34, 90</td><td>13, 49, 122</td><td>17, 64, 185</td><td>22, 83, 249</td></tr><tr><th>15</th><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 5, 7</td><td>3, 6, 14</td><td>3, 9, 25</td><td>4, 16, 41</td><td>5, 24, 58</td><td>8, 33, 89</td><td>12, 48, 121</td><td>16, 63, 184</td><td>22, 82, 248</td></tr><tr><th>16</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 13</td><td>3, 8, 24</td><td>4, 15, 40</td><td>5, 23, 57</td><td>8, 32, 88</td><td>11, 47, 121</td><td>16, 63, 183</td><td>21, 81, 248</td></tr><tr><th>17</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 6, 12</td><td>3, 8, 24</td><td>4, 15, 39</td><td>5, 23, 56</td><td>7, 32, 88</td><td>10, 47, 120</td><td>15, 62, 183</td><td>20, 81, 247</td></tr><tr><th>18</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 6, 12</td><td>3, 7, 23</td><td>4, 14, 39</td><td>5, 22, 55</td><td>6, 31, 87</td><td>10, 46, 119</td><td>14, 61, 182</td><td>19, 80, 246</td></tr><tr><th>19</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 6, 11</td><td>3, 7, 22</td><td>4, 13, 38</td><td>5, 21, 55</td><td>6, 30, 86</td><td>9, 45, 118</td><td>13, 60, 181</td><td>19, 79, 245</td></tr><tr><th>20</th><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 10</td><td>4, 7, 21</td><td>4, 12, 37</td><td>5, 20, 54</td><td>6, 29, 85</td><td>8, 44, 118</td><td>13, 59, 180</td><td>18, 78, 244</td></tr><tr><th>21</th><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 10</td><td>4, 7, 21</td><td>4, 12, 36</td><td>5, 20, 53</td><td>6, 29, 85</td><td>8, 44, 117</td><td>12, 59, 179</td><td>17, 78, 244</td></tr><tr><th>22</th><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 10</td><td>4, 7, 20</td><td>4, 11, 35</td><td>5, 19, 52</td><td>5, 28, 84</td><td>7, 43, 116</td><td>11, 58, 179</td><td>16, 77, 243</td></tr><tr><th>23</th><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 10</td><td>4, 7, 19</td><td>4, 10, 35</td><td>5, 18, 52</td><td>5, 27, 83</td><td>7, 42, 115</td><td>10, 57, 178</td><td>16, 76, 242</td></tr><tr><th>24</th><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 7, 9</td><td>4, 7, 18</td><td>4, 9, 34</td><td>5, 17, 51</td><td>6, 27, 82</td><td>7, 41, 115</td><td>10, 56, 177</td><td>15, 76, 241</td></tr><tr><th>25</th><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 18</td><td>4, 9, 33</td><td>5, 17, 50</td><td>6, 26, 82</td><td>7, 41, 114</td><td>9, 56, 176</td><td>14, 75, 241</td></tr></tbody></table> <h3 id="unit-loss-with-5-defenders"><a href="#unit-loss-with-5-defenders"></a>Unit loss with 5 defenders</h3> <p>This is a table of the attacking unit loss given that there are 5 defenders.</p> <table class="large"><thead><tr><th></th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th><th>9</th><th>10</th><th>11</th><th>12</th></tr></thead><tbody><tr><th>1</th><td>6, 7, 8</td><td>7, 9, 10</td><td>8, 11, 14</td><td>9, 13, 18</td><td>11, 17, 26</td><td>13, 22, 38</td><td>15, 29, 53</td><td>17, 37, 70</td><td>21, 46, 102</td><td>25, 61, 134</td><td>29, 77, 197</td><td>34, 94, 261</td></tr><tr><th>2</th><td>5, 7, 7</td><td>6, 8, 9</td><td>7, 10, 13</td><td>9, 13, 17</td><td>10, 17, 25</td><td>12, 21, 37</td><td>14, 28, 53</td><td>17, 36, 69</td><td>20, 45, 101</td><td>24, 60, 133</td><td>28, 76, 196</td><td>33, 93, 260</td></tr><tr><th>3</th><td>5, 6, 7</td><td>6, 7, 9</td><td>7, 9, 12</td><td>8, 12, 17</td><td>9, 16, 24</td><td>11, 20, 36</td><td>13, 28, 52</td><td>16, 36, 68</td><td>20, 44, 100</td><td>23, 60, 132</td><td>28, 75, 196</td><td>32, 92, 260</td></tr><tr><th>4</th><td>4, 5, 6</td><td>5, 7, 8</td><td>6, 8, 11</td><td>7, 11, 16</td><td>9, 15, 24</td><td>11, 19, 35</td><td>12, 27, 51</td><td>15, 35, 68</td><td>19, 43, 99</td><td>22, 59, 131</td><td>27, 74, 195</td><td>32, 92, 259</td></tr><tr><th>5</th><td>3, 4, 5</td><td>4, 6, 7</td><td>5, 8, 11</td><td>6, 10, 15</td><td>8, 14, 23</td><td>10, 19, 35</td><td>12, 26, 50</td><td>14, 34, 67</td><td>18, 43, 99</td><td>21, 58, 131</td><td>26, 74, 194</td><td>31, 91, 258</td></tr><tr><th>6</th><td>2, 4, 5</td><td>3, 5, 6</td><td>4, 7, 10</td><td>6, 10, 15</td><td>7, 13, 22</td><td>9, 18, 34</td><td>11, 25, 50</td><td>14, 33, 66</td><td>17, 42, 98</td><td>21, 57, 130</td><td>25, 73, 193</td><td>30, 90, 257</td></tr><tr><th>7</th><td>2, 3, 4</td><td>3, 4, 6</td><td>4, 6, 9</td><td>5, 9, 14</td><td>6, 13, 22</td><td>8, 17, 33</td><td>10, 24, 49</td><td>13, 32, 65</td><td>16, 41, 97</td><td>20, 56, 129</td><td>25, 72, 192</td><td>29, 90, 257</td></tr><tr><th>8</th><td>2, 3, 4</td><td>2, 4, 5</td><td>3, 5, 9</td><td>4, 8, 13</td><td>6, 12, 21</td><td>7, 16, 32</td><td>9, 24, 48</td><td>12, 32, 65</td><td>16, 40, 96</td><td>19, 56, 129</td><td>24, 71, 192</td><td>29, 89, 256</td></tr><tr><th>9</th><td>2, 3, 4</td><td>2, 4, 5</td><td>3, 5, 8</td><td>4, 7, 12</td><td>5, 11, 20</td><td>7, 16, 32</td><td>9, 23, 47</td><td>11, 31, 64</td><td>15, 40, 96</td><td>18, 55, 128</td><td>23, 70, 191</td><td>28, 88, 255</td></tr><tr><th>10</th><td>2, 3, 5</td><td>2, 3, 5</td><td>2, 4, 7</td><td>3, 7, 12</td><td>4, 10, 19</td><td>6, 15, 31</td><td>8, 22, 47</td><td>11, 30, 63</td><td>14, 39, 95</td><td>18, 54, 127</td><td>22, 70, 190</td><td>27, 88, 254</td></tr><tr><th>11</th><td>2, 3, 5</td><td>2, 3, 5</td><td>2, 4, 6</td><td>3, 6, 11</td><td>4, 10, 19</td><td>5, 14, 30</td><td>7, 21, 46</td><td>10, 29, 62</td><td>13, 38, 94</td><td>17, 53, 126</td><td>21, 69, 189</td><td>27, 87, 253</td></tr><tr><th>12</th><td>2, 4, 5</td><td>2, 4, 5</td><td>2, 4, 6</td><td>3, 5, 10</td><td>3, 9, 18</td><td>5, 13, 29</td><td>6, 20, 45</td><td>9, 29, 62</td><td>13, 37, 93</td><td>16, 53, 126</td><td>21, 68, 188</td><td>26, 86, 253</td></tr><tr><th>13</th><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 6</td><td>3, 5, 10</td><td>3, 8, 17</td><td>4, 13, 29</td><td>6, 20, 44</td><td>8, 28, 61</td><td>12, 37, 93</td><td>15, 52, 125</td><td>20, 67, 188</td><td>25, 85, 252</td></tr><tr><th>14</th><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 6</td><td>3, 5, 9</td><td>3, 7, 16</td><td>4, 12, 28</td><td>5, 19, 44</td><td>8, 27, 60</td><td>11, 36, 92</td><td>15, 51, 124</td><td>19, 66, 187</td><td>24, 85, 251</td></tr><tr><th>15</th><td>2, 4, 6</td><td>2, 4, 6</td><td>2, 4, 6</td><td>3, 5, 8</td><td>3, 7, 16</td><td>4, 11, 27</td><td>5, 18, 43</td><td>7, 26, 60</td><td>10, 35, 91</td><td>14, 50, 123</td><td>18, 65, 186</td><td>24, 84, 250</td></tr><tr><th>16</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 8</td><td>3, 6, 15</td><td>4, 10, 26</td><td>5, 17, 42</td><td>6, 25, 59</td><td>9, 34, 90</td><td>13, 49, 123</td><td>18, 65, 185</td><td>23, 83, 250</td></tr><tr><th>17</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 8</td><td>3, 6, 14</td><td>4, 10, 26</td><td>4, 17, 41</td><td>6, 25, 58</td><td>9, 34, 90</td><td>12, 49, 122</td><td>17, 64, 185</td><td>22, 83, 249</td></tr><tr><th>18</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 8</td><td>3, 6, 14</td><td>4, 9, 25</td><td>4, 16, 41</td><td>6, 24, 57</td><td>8, 33, 89</td><td>12, 48, 121</td><td>16, 63, 184</td><td>21, 82, 248</td></tr><tr><th>19</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 8</td><td>3, 6, 13</td><td>4, 8, 24</td><td>4, 15, 40</td><td>5, 23, 57</td><td>7, 32, 88</td><td>11, 47, 120</td><td>15, 62, 183</td><td>21, 81, 247</td></tr><tr><th>20</th><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 12</td><td>4, 8, 23</td><td>4, 14, 39</td><td>5, 22, 56</td><td>7, 31, 87</td><td>10, 46, 120</td><td>15, 61, 182</td><td>20, 80, 246</td></tr><tr><th>21</th><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>4, 6, 11</td><td>4, 7, 23</td><td>4, 14, 38</td><td>5, 22, 55</td><td>6, 31, 87</td><td>9, 46, 119</td><td>14, 61, 181</td><td>19, 80, 246</td></tr><tr><th>22</th><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 11</td><td>4, 7, 22</td><td>4, 13, 37</td><td>5, 21, 54</td><td>6, 30, 86</td><td>9, 45, 118</td><td>13, 60, 181</td><td>18, 79, 245</td></tr><tr><th>23</th><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 10</td><td>4, 7, 21</td><td>4, 12, 37</td><td>5, 20, 54</td><td>6, 29, 85</td><td>8, 44, 117</td><td>12, 59, 180</td><td>18, 78, 244</td></tr><tr><th>24</th><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 7, 10</td><td>4, 7, 20</td><td>5, 11, 36</td><td>5, 19, 53</td><td>6, 29, 84</td><td>8, 43, 117</td><td>12, 58, 179</td><td>17, 78, 243</td></tr><tr><th>25</th><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 10</td><td>4, 7, 20</td><td>5, 11, 35</td><td>5, 19, 52</td><td>6, 28, 84</td><td>7, 43, 116</td><td>11, 58, 178</td><td>16, 77, 243</td></tr></tbody></table> <h3 id="unit-loss-with-8-defenders"><a href="#unit-loss-with-8-defenders"></a>Unit loss with 8 defenders</h3> <p>This is a table of the attacking unit loss given that there are 8 defenders.</p> <table class="large"><thead><tr><th></th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th><th>8</th><th>9</th><th>10</th><th>11</th><th>12</th></tr></thead><tbody><tr><th>1</th><td>9, 10, 11</td><td>10, 12, 13</td><td>11, 14, 17</td><td>12, 16, 21</td><td>14, 20, 29</td><td>16, 25, 41</td><td>18, 32, 56</td><td>20, 40, 73</td><td>24, 49, 105</td><td>28, 64, 137</td><td>32, 80, 200</td><td>37, 97, 264</td></tr><tr><th>2</th><td>8, 10, 10</td><td>9, 11, 12</td><td>10, 13, 16</td><td>12, 16, 20</td><td>13, 20, 28</td><td>15, 24, 40</td><td>17, 31, 56</td><td>20, 39, 72</td><td>23, 48, 104</td><td>27, 63, 136</td><td>31, 79, 199</td><td>36, 96, 263</td></tr><tr><th>3</th><td>8, 9, 10</td><td>9, 10, 12</td><td>10, 12, 15</td><td>11, 15, 20</td><td>12, 19, 27</td><td>14, 23, 39</td><td>16, 31, 55</td><td>19, 39, 71</td><td>23, 47, 103</td><td>26, 63, 135</td><td>31, 78, 199</td><td>35, 95, 263</td></tr><tr><th>4</th><td>7, 8, 9</td><td>8, 10, 11</td><td>9, 11, 14</td><td>10, 14, 19</td><td>12, 18, 27</td><td>14, 22, 38</td><td>15, 30, 54</td><td>18, 38, 71</td><td>22, 46, 102</td><td>25, 62, 134</td><td>30, 77, 198</td><td>35, 95, 262</td></tr><tr><th>5</th><td>6, 7, 8</td><td>7, 9, 10</td><td>8, 11, 14</td><td>9, 13, 18</td><td>11, 17, 26</td><td>13, 22, 38</td><td>15, 29, 53</td><td>17, 37, 70</td><td>21, 46, 102</td><td>24, 61, 134</td><td>29, 77, 197</td><td>34, 94, 261</td></tr><tr><th>6</th><td>5, 7, 8</td><td>6, 8, 9</td><td>7, 10, 13</td><td>9, 13, 18</td><td>10, 16, 25</td><td>12, 21, 37</td><td>14, 28, 53</td><td>17, 36, 69</td><td>20, 45, 101</td><td>24, 60, 133</td><td>28, 76, 196</td><td>33, 93, 260</td></tr><tr><th>7</th><td>5, 6, 7</td><td>6, 7, 9</td><td>7, 9, 12</td><td>8, 12, 17</td><td>9, 16, 25</td><td>11, 20, 36</td><td>13, 27, 52</td><td>16, 35, 68</td><td>19, 44, 100</td><td>23, 59, 132</td><td>28, 75, 195</td><td>32, 93, 260</td></tr><tr><th>8</th><td>4, 5, 6</td><td>5, 7, 8</td><td>6, 8, 12</td><td>7, 11, 16</td><td>9, 15, 24</td><td>10, 19, 35</td><td>12, 27, 51</td><td>15, 35, 68</td><td>19, 43, 99</td><td>22, 59, 132</td><td>27, 74, 195</td><td>32, 92, 259</td></tr><tr><th>9</th><td>3, 5, 6</td><td>4, 6, 7</td><td>5, 8, 11</td><td>6, 10, 15</td><td>8, 14, 23</td><td>10, 19, 35</td><td>12, 26, 50</td><td>14, 34, 67</td><td>18, 43, 99</td><td>21, 58, 131</td><td>26, 73, 194</td><td>31, 91, 258</td></tr><tr><th>10</th><td>3, 4, 5</td><td>3, 5, 7</td><td>4, 7, 10</td><td>6, 10, 15</td><td>7, 13, 22</td><td>9, 18, 34</td><td>11, 25, 50</td><td>14, 33, 66</td><td>17, 42, 98</td><td>21, 57, 130</td><td>25, 73, 193</td><td>30, 91, 257</td></tr><tr><th>11</th><td>2, 4, 5</td><td>3, 5, 6</td><td>4, 6, 9</td><td>5, 9, 14</td><td>6, 13, 22</td><td>8, 17, 33</td><td>10, 24, 49</td><td>13, 32, 65</td><td>16, 41, 97</td><td>20, 56, 129</td><td>24, 72, 192</td><td>30, 90, 256</td></tr><tr><th>12</th><td>2, 4, 5</td><td>3, 4, 6</td><td>3, 6, 9</td><td>4, 8, 13</td><td>6, 12, 21</td><td>7, 16, 32</td><td>9, 23, 48</td><td>12, 32, 65</td><td>16, 40, 96</td><td>19, 56, 129</td><td>24, 71, 191</td><td>29, 89, 256</td></tr><tr><th>13</th><td>2, 4, 6</td><td>3, 4, 6</td><td>3, 5, 8</td><td>4, 7, 13</td><td>5, 11, 20</td><td>7, 16, 32</td><td>9, 23, 47</td><td>11, 31, 64</td><td>15, 40, 96</td><td>18, 55, 128</td><td>23, 70, 191</td><td>28, 88, 255</td></tr><tr><th>14</th><td>2, 4, 6</td><td>2, 4, 6</td><td>3, 5, 7</td><td>4, 7, 12</td><td>4, 10, 19</td><td>6, 15, 31</td><td>8, 22, 47</td><td>11, 30, 63</td><td>14, 39, 95</td><td>18, 54, 127</td><td>22, 69, 190</td><td>27, 88, 254</td></tr><tr><th>15</th><td>2, 4, 6</td><td>2, 4, 6</td><td>3, 5, 7</td><td>3, 6, 11</td><td>4, 10, 19</td><td>5, 14, 30</td><td>7, 21, 46</td><td>10, 29, 63</td><td>13, 38, 94</td><td>17, 53, 126</td><td>21, 68, 189</td><td>27, 87, 253</td></tr><tr><th>16</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 6, 10</td><td>4, 9, 18</td><td>5, 13, 29</td><td>7, 20, 45</td><td>9, 28, 62</td><td>12, 37, 93</td><td>16, 52, 126</td><td>21, 68, 188</td><td>26, 86, 253</td></tr><tr><th>17</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 6, 10</td><td>4, 8, 17</td><td>5, 13, 29</td><td>6, 20, 44</td><td>9, 28, 61</td><td>12, 37, 93</td><td>15, 52, 125</td><td>20, 67, 188</td><td>25, 86, 252</td></tr><tr><th>18</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 6, 9</td><td>4, 8, 17</td><td>4, 12, 28</td><td>6, 19, 44</td><td>8, 27, 60</td><td>11, 36, 92</td><td>15, 51, 124</td><td>19, 66, 187</td><td>24, 85, 251</td></tr><tr><th>19</th><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 5, 7</td><td>3, 6, 9</td><td>4, 7, 16</td><td>4, 11, 27</td><td>5, 18, 43</td><td>7, 26, 60</td><td>10, 35, 91</td><td>14, 50, 123</td><td>18, 65, 186</td><td>24, 84, 250</td></tr><tr><th>20</th><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 9</td><td>4, 7, 15</td><td>4, 10, 26</td><td>5, 17, 42</td><td>7, 25, 59</td><td>9, 34, 90</td><td>13, 49, 123</td><td>18, 64, 185</td><td>23, 83, 249</td></tr><tr><th>21</th><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 8</td><td>3, 6, 9</td><td>4, 7, 14</td><td>4, 10, 26</td><td>5, 17, 41</td><td>6, 25, 58</td><td>9, 34, 90</td><td>12, 49, 122</td><td>17, 64, 184</td><td>22, 83, 249</td></tr><tr><th>22</th><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 9</td><td>4, 7, 14</td><td>4, 9, 25</td><td>5, 16, 40</td><td>6, 24, 57</td><td>8, 33, 89</td><td>12, 48, 121</td><td>16, 63, 184</td><td>21, 82, 248</td></tr><tr><th>23</th><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 8</td><td>4, 6, 9</td><td>4, 7, 13</td><td>4, 9, 24</td><td>5, 15, 40</td><td>6, 23, 57</td><td>8, 32, 88</td><td>11, 47, 120</td><td>15, 62, 183</td><td>21, 81, 247</td></tr><tr><th>24</th><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 6, 9</td><td>4, 7, 12</td><td>4, 8, 23</td><td>5, 14, 39</td><td>6, 22, 56</td><td>7, 32, 87</td><td>10, 46, 120</td><td>15, 61, 182</td><td>20, 81, 246</td></tr><tr><th>25</th><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 9</td><td>4, 7, 12</td><td>4, 8, 23</td><td>5, 13, 38</td><td>6, 22, 55</td><td>7, 31, 87</td><td>9, 46, 119</td><td>14, 61, 181</td><td>19, 80, 246</td></tr></tbody></table> How to Build a Personal Webpage from Scratch 2022-02-16T00:00:00+00:00 2022-02-16T00:00:00+00:00 https://rutar.org/writing/how-to-build-a-personal-webpage-from-scratch/ <h2 id="an-overview-of-static-webpage-development"><a href="#an-overview-of-static-webpage-development"></a>An overview of static webpage development</h2> <h3 id="what-is-a-static-webpage"><a href="#what-is-a-static-webpage"></a>What is a static webpage?</h3> <p>Modern webpages are (primarily) composed of three types of files: HTML, CSS, and JavaScript. Basically,</p> <ul> <li>HTML specifies the <em>content</em>,</li> <li>CSS defines the <em>style</em>, and</li> <li>JavaScript allows <em>dynamic behaviour</em>.</li> </ul> <p>Webpages also consist of additional resources, such as images or documents. When you visit a webpage, your web browser requests the documents from a server.</p> <p><a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Static_web_page">Static</a> and <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Dynamic_web_page">dynamic</a> webpages differ in how the files are prepared before they are sent to the visitor. A static webpage is essentially a <em>collection of files</em> sitting on the server, which are sent directly to the webpage visitor, whereas a (server-side<label for="in1">1</label><input type="checkbox" id="in1"><small>This is in contrast to a client-side dynamic webpage which uses JavaScript, but only in your web browser.</small>) dynamic webpage typically consists of a database as well as code to generate the files <em>on the fly</em><label for="in2">2</label><input type="checkbox" id="in2"><small>This code can either run in your browser, or the page rendering itself can happen on the server.</small> when they are requested by the visitor.</p> <p>For example, this website is a static webpage. On the other hand, any webpage which allows you to log-in and have user-specific state is a dynamic webpage. The majority of web pages on the internet are dynamic webpages.</p> <p>When building a static webpage, you can simply prepare the entire webpage as a directory containing files. In fact, you can make a webpage (using your own device as the server) simply by writing a bit of HTML with no CSS or JavaScript: create a file named <code>index.html</code> with the contents</p> <pre data-lang="html" class="language-html z-code"><code class="language-html" data-lang="html"><span class="z-text z-html z-basic"><span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-structure z-any z-html">html</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span><span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-structure z-any z-html">body</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>content<span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-structure z-any z-html">body</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span><span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-structure z-any z-html">html</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span></code></pre> <p>and, when you double-click on the file, it should open in your browser and show a (rather minimal) page displaying the line “content”.</p> <p>Dynamic webpages are built in a similar way, except rather than generating the pages in advance, the files sent to the visitor are generated as they are requested. A dynamic webpage will typically have a <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Database">database</a> used to store the site information, and a web server which uses the database to generate content when requested by the user. This allows substantially more flexibility in content delivery (e.g. webpage state, customization for the viewer, etc.) since the content delivered to the webpage can depend on arbitrary variables whose values are unknown when creating the site. This also allows procedural generation of content, which avoids repetitive code. The main downside is that, since the pages need to be generated for each visitor, dynamic websites are typically more resource intensive and slower to render.</p> <h3 id="why-static-webpages"><a href="#why-static-webpages"></a>Why static webpages?</h3> <p>However, for a personal blog or site used primarily to <em>distribute</em> information, rather than collect it, dynamic content generation is not necessary! Here is a short list of reasons why you should prefer simple static webpages (if you do not already know that you require a dynamic webpage):</p> <ul> <li><strong>Longevity.</strong> A static webpage is just a collection of files, which you should also have saved on your computer. So even in the worst case scenario—say your web server disappears—you still have the files for your webpage and you can just put them up somewhere else. Static webpages are also easier to maintain: if you have a dynamic webpage, you need to keep the required tools up to date otherwise everything will cease to work.</li> <li><strong>Portability.</strong> If you decide that you want to host your webpage somewhere else, this is straightforward to do. Static webpages are also simple enough that you can host them yourself on a low-power device!</li> <li><strong>Security.</strong> Since static webpages are substantially simpler, they have a smaller attack surface and are more secure. There is no underlying server serving requests, or processing user input. Even in hosted situations, there can be problems with the underlying content management system. For example, many Wordpress sites <a rel="noopener" target="_blank" href="https://www.bleepingcomputer.com/news/security/over-90-wordpress-themes-plugins-backdoored-in-supply-chain-attack/">were compromised</a> through an exploit which targeted the plugin system.</li> </ul> <h3 id="template-processing"><a href="#template-processing"></a>Template processing</h3> <p>One downside of manually preparing a webpage is that there is often a large amount of repetition: for example, each <a href="/tags/">article</a> on this webpage has different content but many shared layout features. In order to simplify this process, a common technique is to use a <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Template_processor">template processor</a>. This is a program which takes files written in a templating language (such as <a rel="noopener" target="_blank" href="https://jinja.palletsprojects.com/en/3.0.x/templates/">Jinja</a> or <a rel="noopener" target="_blank" href="https://tera.netlify.app/">Tera</a>), combined with content files written in a markup language (such as <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>) and converts them into HTML, CSS, and JavaScript files. You first prepare the templates, then write the content files, and then use the template processor to generate the webpage files.</p> <h3 id="static-website-components"><a href="#static-website-components"></a>Static website components</h3> <p>There are three main components to static website generation:</p> <ol> <li><strong>Templating</strong>. You should choose a templating engine and then prepare the website template. I am personally a fan of <a rel="noopener" target="_blank" href="https://getzola.org">Zola</a>; some other common options are <a rel="noopener" target="_blank" href="https://jekyllrb.com/">Jekyll</a> and <a rel="noopener" target="_blank" href="https://gohugo.io/">Hugo</a>.</li> <li><strong>Content</strong>. The most important part of a webpage is the content! Beyond that, it’s nice if your webpage also looks decent. You need to write base HTML files and CSS style sheets, as well as the webpage content.</li> <li><strong>Deployment</strong>. While it is possible (and not too difficult) to host your own static site server, it is typically easiest to use static site hosting. I use <a rel="noopener" target="_blank" href="https://pages.cloudflare.com/">Cloudflare Pages</a>. You might also be interested in <a rel="noopener" target="_blank" href="https://pages.github.com/">GitHub Pages</a> or <a rel="noopener" target="_blank" href="https://www.netlify.com/">Netlify</a>.</li> </ol> <p>Of course, templating is mainly a convenience feature, and not strictly necessary when making a small website. I’ll discuss the other two components in the following sections, along with some additional topics in the <a href="https://rutar.org/writing/how-to-build-a-personal-webpage-from-scratch/#further-topics">final section</a>.</p> <h3 id="a-note-on-editing-text"><a href="#a-note-on-editing-text"></a>A note on editing text</h3> <p>HTML is a <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Markup_language">markup language</a>, which means that the text represents content, rather than being the content visually. Other well-known markup languages include <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/LaTeX">LaTeX</a> and <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>.</p> <p>Therefore when writing content for your webpage, it is important to use an editor which accurately represents the contents of the file you are editing. This is in contrast to software such as Microsoft Word, in which the content that you enter on the page is different than the underlying representation. Some popular graphical text editors include <a rel="noopener" target="_blank" href="https://atom.io">Atom</a> and <a rel="noopener" target="_blank" href="https://www.sublimetext.com">Sublime Text</a>. If you do not want to install anything, <a rel="noopener" target="_blank" href="https://www.microsoft.com/en-us/p/windows-notepad/9msmlrh6lzf3">Notepad</a> is a built-in text editor on Windows. Similarly, <a rel="noopener" target="_blank" href="https://support.apple.com/en-ca/guide/textedit/welcome/mac">TextEdit</a> is built-in on macOS. If you prefer a command-line interface, you could consider <a rel="noopener" target="_blank" href="https://neovim.io">Neovim</a> or <a rel="noopener" target="_blank" href="https://www.gnu.org/software/emacs/">Emacs</a>.</p> <p>One subtlety is that not all text is the same. Underneath, text is just binary data, so rules are required to convert the binary data into the textual representation: this process is known as <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Character_encoding">character encoding</a>. The most common type of encoding used on webpages is <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a>, which is the transfer format for the <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Unicode">Unicode</a> standard. <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/ASCII">ASCII</a> is also a well-known encoding, but only supports a very restricted number of characters. Certain older software, such as <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/TeX">TeX</a>, defaults to files encoded in ASCII:<label for="in3">3</label><input type="checkbox" id="in3"><small>If you <code>\usepackage[utf8]{inputenc}</code>, you can use Unicode directly in the .tex file.</small> for example, to input directional quotation marks <code>“”</code> (which are <a rel="noopener" target="_blank" href="https://unicode-table.com/en/201C/">Left Double Quotation Mark</a> and <a rel="noopener" target="_blank" href="https://unicode-table.com/en/201D/">Right Double Quotation Mark</a> respectively), one would use <code>``</code> and <code>''</code>. However, unless you are forced otherwise, you should try to write all your content in Unicode.</p> <h2 id="crash-course-in-html-and-css"><a href="#crash-course-in-html-and-css"></a>Crash course in HTML and CSS</h2> <p>I’m going to assume you know some basics of HTML and CSS. There are lots of tutorials online; here is a <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/">nice one</a>. I’d recommend you read the articles on <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics">HTML basics</a> and <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics">CSS basics</a>.</p> <p>In this section, I’ll give a complete (albeit streamlined) description of setting up a basic functional webpage. We are going to create 4 files:</p> <pre class="z-code"><code><span class="z-text z-plain">. </span><span class="z-text z-plain">├── 404.html </span><span class="z-text z-plain">├── index.html </span><span class="z-text z-plain">├── style.css </span><span class="z-text z-plain">└── writing </span><span class="z-text z-plain"> └── index.html </span></code></pre> <h3 id="getting-started"><a href="#getting-started"></a>Getting started</h3> <p>Let’s start with a rather minimal HTML file. Call it <code>index.html</code> in your (currently empty) website folder.</p> <pre data-lang="html" class="language-html z-code"><code class="language-html" data-lang="html"><span class="z-text z-html z-basic"><span class="z-meta z-tag z-sgml z-doctype z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;!</span><span class="z-keyword z-declaration z-doctype z-html">DOCTYPE</span> <span class="z-constant z-language z-doctype z-html">html</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"><span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-structure z-any z-html">html</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">lang</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>en<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-structure z-any z-html">head</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">meta</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">charset</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>utf-8<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">meta</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">name</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>viewport<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">content</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>width=device-width<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">title</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>Example Webpage<span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-inline z-any z-html">title</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">meta</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">name</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>description<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">content</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>An example webpage.<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">meta</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">name</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>author<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">content</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>Alex Rutar<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-structure z-any z-html">head</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-structure z-any z-html">body</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">header</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> Example Webpage </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">header</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">nav</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">href</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>writing/index.html<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>Writing<span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">href</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>index.html<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>About<span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">nav</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">article</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">h1</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>Welcome to my webpage<span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">h1</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">p</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>This is some content!<span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">p</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">article</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-structure z-any z-html">body</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"><span class="z-meta z-tag z-structure z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-structure z-any z-html">html</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span></code></pre> <p>This isn’t the most bare-bones possible HTML file, but it is a good <em>modern</em> starting point for all HTML content on your site. Here’s an explanation of some of the tags:</p> <ul> <li><code>&lt;head&gt;...&lt;/head&gt;</code> and <code>&lt;body&gt;...&lt;/body&gt;</code>: these are the two main sections of your HTML file. <code>&lt;head&gt;</code> contains the metadata, and <code>&lt;body&gt;</code> contains the content that will show up on your screen when you visit the webpage.</li> <li><code>&lt;meta charset="utf-8"&gt;</code>: declare that the content is encoded in <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a>.<label for="in4">4</label><input type="checkbox" id="in4"><small>You also need to make sure the file you are editing has the correct encoding!</small></li> <li><code>&lt;meta name="viewport" content="width=device-width"&gt;</code> ensures that, if this page is opened on a browser with a small screen, it will not be incredibly zoomed out. This is the bare minimum required so your page looks passable on a phone. You can read a bit about this <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag">here</a>.</li> <li><code>&lt;title&gt;</code>, <code>&lt;meta name = "description" ...&gt;</code>, <code>&lt;meta name="author" ...&gt;</code>: title, a short page description, and author.</li> <li><code>&lt;nav&gt;...&lt;/nav&gt;</code>: website navigation</li> <li><code>&lt;article&gt;...&lt;/article&gt;</code>: the ‘content’ of the page, i.e. your actual article or blog post but without the navigation, header, footer, etc.</li> </ul> <p>Now if you open the file <code>index.html</code> with your web browser (you can probably just double-click it) you should get a page with two links at the top: <strong>About</strong>, and <strong>Writing</strong>.</p> <p>Unfortunately the <strong>Writing</strong> link does nothing: we need to create that page. Create a new directory called <code>writing</code> and in that directory create a file called <code>index.html</code>. Then, fill it with pretty much the same contents as <code>index.html</code>, but replace the <code>&lt;nav&gt;...&lt;/nav&gt;</code> with the contents</p> <pre data-lang="html" class="language-html z-code"><code class="language-html" data-lang="html"><span class="z-text z-html z-basic"><span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">nav</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">href</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>index.html<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>Writing<span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">href</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>../index.html<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>About<span class="z-meta z-tag z-inline z-a z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-inline z-a z-html">a</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"><span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">nav</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span></code></pre> <p>and replace the <code>&lt;article&gt;...&lt;/article&gt;</code> with something slightly different, say</p> <pre data-lang="html" class="language-html z-code"><code class="language-html" data-lang="html"><span class="z-text z-html z-basic"><span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">article</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">h1</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>Some things I&#39;ve written<span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">h1</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"> <span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-block z-any z-html">p</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span>Nothing here yet...<span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">p</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span><span class="z-text z-html z-basic"><span class="z-meta z-tag z-block z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;/</span><span class="z-entity z-name z-tag z-block z-any z-html">article</span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span></code></pre> <p>You can also change the title and description in this new page, as well. Now, if you click on the navigation links, you have two working pages!</p> <h3 id="styling-the-page"><a href="#styling-the-page"></a>Styling the page</h3> <p>We have a functional webpage, but it would be nice to make everything look a bit better. Create a file <code>style.css</code>, and add the line</p> <pre data-lang="html" class="language-html z-code"><code class="language-html" data-lang="html"><span class="z-text z-html z-basic"><span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">link</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">rel</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>stylesheet<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">type</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>text/css<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">href</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>style.css<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span></code></pre> <p>to the <code>&lt;head&gt;</code> of <code>index.html</code> and</p> <pre data-lang="html" class="language-html z-code"><code class="language-html" data-lang="html"><span class="z-text z-html z-basic"><span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">link</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">rel</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>stylesheet<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">type</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>text/css<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">href</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>../style.css<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span></code></pre> <p>to the <code>&lt;head&gt;</code> of <code>writing/index.html</code>. This tells the browser to look for a file named <code>style.css</code> in the same directory as <code>index.html</code> and in the directory containing the file <code>writing/index.html</code>, which contains styling information. While it is possible to define styles inline in the HTML, this is bad practice since it is harder to maintain (as a general rule, HTML defines semantics, whereas CSS defines style). Also, you should add some more content to the <code>&lt;article&gt;</code> section: perhaps a few more headers <code>&lt;h2&gt;...&lt;/h2&gt;</code> and links <code>&lt;a href="...some url..."&gt;my link&lt;/a&gt;</code>. This will make the styling changes more clear as you make them.</p> <p>Let’s begin with some basic styling. First, add</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">body</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">margin</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">0</span> <span class="z-support z-constant z-property-value z-css">auto</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">max-width</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">700<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">min-width</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">0</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">padding</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">0</span> <span class="z-constant z-numeric z-integer z-decimal z-css">10<span class="z-keyword z-other z-unit z-css">px</span></span> <span class="z-constant z-numeric z-integer z-decimal z-css">25<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">font-family</span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-string z-quoted z-double z-css"><span class="z-punctuation z-definition z-string z-begin z-css">&quot;</span>Helvetica<span class="z-punctuation z-definition z-string z-end z-css">&quot;</span></span><span class="z-punctuation z-separator z-sequence z-css">,</span> <span class="z-string z-quoted z-double z-css"><span class="z-punctuation z-definition z-string z-begin z-css">&quot;</span>Arial<span class="z-punctuation z-definition z-string z-end z-css">&quot;</span></span><span class="z-punctuation z-separator z-sequence z-css">,</span> <span class="z-support z-constant z-font-name z-css">sans-serif</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span></span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>to the file <code>style.css</code>. This centres the text body, prevents it from being too wide on large screens, ensures there is a bit of a space on the boundary when the screen is small, and finally sets a new font (rather than the usual default Times New Roman). The <code>min-width: 0</code> is useful to prevent large elements from (accidentally) making the page very wide on screens narrower than 700 pixels.</p> <p>We can also adjust the spacing so that the text is laid out a bit more nicely:</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">h2</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">margin-top</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">1<span class="z-keyword z-other z-unit z-css">em</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">padding-top</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">1<span class="z-keyword z-other z-unit z-css">em</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">nav</span> <span class="z-entity z-name z-tag z-css">a</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">margin-left</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">20<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>And adjust the colour of the text itself to something a bit more pleasant:</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">body</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">color</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-other z-color z-rgb-value z-css"><span class="z-punctuation z-definition z-constant z-css">#</span>444</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">h1</span><span class="z-punctuation z-separator z-sequence z-css">,</span> <span class="z-entity z-name z-tag z-css">h2</span><span class="z-punctuation z-separator z-sequence z-css">,</span> <span class="z-entity z-name z-tag z-css">strong</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">color</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-other z-color z-rgb-value z-css"><span class="z-punctuation z-definition z-constant z-css">#</span>222</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>Then touch up the font sizes:</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">header</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">margin</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">0<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">font-size</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">23<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">article</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">font-size</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">16<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">nav</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">font-size</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">18<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">letter-spacing</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">1<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">h1</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">font-size</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">26<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">h2</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">font-size</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">23<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>Finally, let’s add a bit of character by styling the links:</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">a</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">color</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-other z-color z-rgb-value z-css"><span class="z-punctuation z-definition z-constant z-css">#</span>ffa64d</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>Our webpage looks a bit cleaner now!</p> <h3 id="grid-layout"><a href="#grid-layout"></a>Grid layout</h3> <p>However, we need to address some more serious layout problems: currently, the navigation is way too small, and the header does not stand out at all.</p> <p>To fix this, we are going to use a relatively new CSS technique known as <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid">CSS Grid</a>.<label for="in5">5</label><input type="checkbox" id="in5"><small>A nice reference for CSS Grid can be found <a rel="noopener" target="_blank" href="https://css-tricks.com/snippets/css/complete-guide-grid/">here</a>.</small> Essentially, CSS Grid allows us to specify layout in a parent element, and then place the children inside this layout.</p> <p>First, let’s specify the general layout of our grid. We want three sections: a header in the top left (for our title), a navigation bar in the top right, and then a main area containing all the content of the site. We will target the <code>&lt;body&gt;</code> element to set up this grid:</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">body</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">display</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">grid</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css">row-gap</span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">5<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-template-columns</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">auto</span> <span class="z-support z-constant z-property-value z-css">auto</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-template-rows</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">60<span class="z-keyword z-other z-unit z-css">px</span></span> <span class="z-support z-constant z-property-value z-css">auto</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-template-areas</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span></span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"><span class="z-meta z-property-value z-css"> <span class="z-string z-quoted z-double z-css"><span class="z-punctuation z-definition z-string z-begin z-css">&quot;</span>header nav<span class="z-punctuation z-definition z-string z-end z-css">&quot;</span></span> </span></span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"><span class="z-meta z-property-value z-css"> <span class="z-string z-quoted z-double z-css"><span class="z-punctuation z-definition z-string z-begin z-css">&quot;</span>ct ct<span class="z-punctuation z-definition z-string z-end z-css">&quot;</span></span> </span></span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"><span class="z-meta z-property-value z-css"></span></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>Let’s break down what this is doing.</p> <ul> <li><code>display: grid</code>: this element is specifying a grid layout for its children.</li> <li><code>row-gap: 5px</code>: have some space between the rows</li> <li><code>grid-template-columns: auto auto</code>: we have two columns, and we want to determine their widths automatically</li> <li><code>grid-template-rows: 60px auto</code>: we have two rows, where the first row (containing the header and navigation bar) has a height of 60 pixels, and the second row is sized automatically based on the content</li> <li><code>grid-template-areas: ...</code>: assign names to the areas. We have <code>header</code> in the top left, <code>nav</code> in the top right, and <code>ct</code> everywhere else (spanning both columns). Note that the names can be whatever you would like (there is no special meaning assigned to using <code>header</code> and <code>nav</code>).</li> </ul> <p>Now, let’s assign our child elements to the areas in this grid, and do a bit of styling.</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">header</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-area</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css">header</span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css">justify-self</span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">left</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">align-self</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">end</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">nav</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-area</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css">nav</span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css">justify-self</span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">right</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">align-self</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">end</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">nav</span> <span class="z-entity z-name z-tag z-css">a</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">text-align</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">right</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">article</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-area</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css">ct</span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">border-top</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">2<span class="z-keyword z-other z-unit z-css">px</span></span> <span class="z-support z-constant z-property-value z-css">solid</span> <span class="z-support z-constant z-color z-w3c-standard-color-name z-css">gray</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>Here is a quick explanation of what this does. First, we want the header to be justified to the left and the navigation bar to be justified to the right. Note that <code>align-self: end</code> means that, within the grid row, we want to be placed as late as possible. This is important since the row has height 60 pixels, and without this argument, our header and navigation bar would be placed adjacent to the top of the screen!<label for="in6">6</label><input type="checkbox" id="in6"><small>In general, <em>align</em> refers to vertical placement and <em>justify</em> refers to horizontal placement.</small> Finally, we add a border above the <code>&lt;article&gt;</code> element with <code>border-top: 2px solid gray</code> to visually separate our header and navigation bar from the rest of the content.</p> <h3 id="responsive-design"><a href="#responsive-design"></a>Responsive design</h3> <p>Our layout looks decent on a computer, but now we might have some problems on small screens! The main problem is that if the screen is narrow, our header and navigation bar will start folding over itself. To fix this, we can use <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries">media queries</a>. This is CSS which is applied only when the query is satisfied: in this situation, we want these rules when the screen is narrow (less than 430 pixels wide).</p> <p>Here, we only change the styling a bit:</p> <ul> <li>place the header on top of the navigation bar by changing the grid layout in the body</li> <li>centre the header and navigation bar</li> <li>remove the asymmetric styling on the navigation links</li> </ul> <p>This is done with the following CSS.</p> <pre data-lang="css" class="language-css z-code"><code class="language-css" data-lang="css"><span class="z-source z-css"><span class="z-meta z-at-rule z-media z-css"><span class="z-keyword z-control z-at-rule z-media z-css"><span class="z-punctuation z-definition z-keyword z-css">@</span>media</span> <span class="z-support z-constant z-media z-css">screen</span> <span class="z-keyword z-operator z-logic z-media z-css">and</span> <span class="z-punctuation z-definition z-group z-begin z-css">(</span><span class="z-support z-type z-property-name z-media z-css">max-width</span><span class="z-punctuation z-separator z-key-value z-css">:</span> <span class="z-constant z-numeric z-integer z-decimal z-css">430<span class="z-keyword z-other z-unit z-css">px</span></span><span class="z-punctuation z-definition z-group z-end z-css">)</span> </span><span class="z-punctuation z-section z-property-list z-css">{</span> </span><span class="z-source z-css"> <span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">body</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-template-columns</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">auto</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-template-rows</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-meta z-function-call z-css"><span class="z-support z-function z-grid z-css">minmax</span><span class="z-meta z-group z-css"><span class="z-punctuation z-definition z-group z-begin z-css">(</span><span class="z-constant z-numeric z-integer z-decimal z-css">40<span class="z-keyword z-other z-unit z-css">px</span></span><span class="z-punctuation z-separator z-sequence z-css">,</span> <span class="z-support z-constant z-property-value z-css">auto</span></span><span class="z-meta z-group z-css"><span class="z-punctuation z-definition z-group z-end z-css">)</span></span></span> <span class="z-meta z-function-call z-css"><span class="z-support z-function z-grid z-css">minmax</span><span class="z-meta z-group z-css"><span class="z-punctuation z-definition z-group z-begin z-css">(</span><span class="z-constant z-numeric z-integer z-decimal z-css">30<span class="z-keyword z-other z-unit z-css">px</span></span><span class="z-punctuation z-separator z-sequence z-css">,</span> <span class="z-support z-constant z-property-value z-css">auto</span></span><span class="z-meta z-group z-css"><span class="z-punctuation z-definition z-group z-end z-css">)</span></span></span> <span class="z-support z-constant z-property-value z-css">auto</span> <span class="z-support z-constant z-property-value z-css">auto</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">grid-template-areas</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span></span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"><span class="z-meta z-property-value z-css"> <span class="z-string z-quoted z-double z-css"><span class="z-punctuation z-definition z-string z-begin z-css">&quot;</span>header<span class="z-punctuation z-definition z-string z-end z-css">&quot;</span></span> </span></span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"><span class="z-meta z-property-value z-css"> <span class="z-string z-quoted z-double z-css"><span class="z-punctuation z-definition z-string z-begin z-css">&quot;</span>nav<span class="z-punctuation z-definition z-string z-end z-css">&quot;</span></span> </span></span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"><span class="z-meta z-property-value z-css"> <span class="z-string z-quoted z-double z-css"><span class="z-punctuation z-definition z-string z-begin z-css">&quot;</span>ct<span class="z-punctuation z-definition z-string z-end z-css">&quot;</span></span> </span></span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"><span class="z-meta z-property-value z-css"> </span></span><span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"> <span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">header</span><span class="z-punctuation z-separator z-sequence z-css">,</span> <span class="z-entity z-name z-tag z-css">nav</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">text-align</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">center</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css">justify-self</span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-support z-constant z-property-value z-css">center</span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span> <span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"> <span class="z-meta z-selector z-css"><span class="z-entity z-name z-tag z-css">nav</span> <span class="z-entity z-name z-tag z-css">a</span> </span><span class="z-meta z-property-list z-css"><span class="z-punctuation z-section z-property-list z-css">{</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"> <span class="z-meta z-property-name z-css"><span class="z-support z-type z-property-name z-css">margin</span></span><span class="z-punctuation z-separator z-key-value z-css">:</span><span class="z-meta z-property-value z-css"> </span><span class="z-meta z-property-value z-css"><span class="z-constant z-numeric z-integer z-decimal z-css">0</span> <span class="z-constant z-numeric z-integer z-decimal z-css">10<span class="z-keyword z-other z-unit z-css">px</span></span></span><span class="z-punctuation z-terminator z-rule z-css">;</span> </span></span><span class="z-source z-css"><span class="z-meta z-property-list z-css"></span> <span class="z-punctuation z-section z-property-list z-css">}</span> </span><span class="z-source z-css"><span class="z-punctuation z-section z-property-list z-css">}</span> </span></code></pre> <p>The <code>minmax(40px, auto)</code> means that we want to row to be at least 40 pixels tall, except that we should make it taller if the elements inside require it. We also adjust the header and navigation bar to be nicely centred, since they are now placed on top of each other (rather than side by side).</p> <p>Now, our webpage also looks respectable even when viewed on exceptionally tiny phone screens.</p> <h3 id="dealing-with-links"><a href="#dealing-with-links"></a>Dealing with links</h3> <p>Throughout this article, the links have been written in the form <code>href="style.css"</code>. This specifies a <em>relative link</em>: the file path is taken relative to the directory that the file in which the link sits. The syntax <code>href="../"</code> specifies that we are referring to a file in the directory containing the current directory.</p> <p>However, when deploying the webpage to a server, you will want to write the links in the form <code>href="/style.css"</code>, which will give a link to the root of your website. This tells the browser to take the base URL (for example <a rel="noopener" target="_blank" class="verbose-url" href="https://example.rutar.org">https://example.rutar.org</a>) and append the link. However, when browsing files on your device, the base URL is the root of your filesystem, i.e. <code>/</code>, so <code>/style.css</code> will (attempt to) link to the root of your filesystem directory, which was not what we wanted!</p> <p>Moreover, there is a convention for linking directly to files HTML files: files with the name <code>index.html</code> are given special treatment. Suppose your directory structure looks like the following:</p> <pre class="z-code"><code><span class="z-text z-plain">. </span><span class="z-text z-plain">├── index.html </span><span class="z-text z-plain">└── writing </span><span class="z-text z-plain"> └── index.html </span></code></pre> <p>Now, you can reference the file <code>index.html</code> with <code>href="/"</code> and the file <code>writing/index.html</code> with <code>href="/writing/"</code>. This is the standard way to include files in your project repository. To summarize, here are the changes we need to make:</p> <ul> <li>in both HTML files, change <code>href="style.css"</code> to <code>href="/style.css"</code></li> <li>in <code>writing/index.html</code>, change <code>href="index.html"</code> to <code>href="/writing/"</code> and <code>href="../index.html"</code> to <code>href="/"</code></li> <li>in <code>index.html</code>, change <code>href="index/writing.html"</code> to <code>href="/writing/"</code> and <code>href="index.html"</code> to <code>href="/"</code></li> </ul> <p>Our links will no longer work properly when browsing the files locally, but when our webpage is online, the links will now work properly. We also want to deal with the case where the user tries to browse to a link which does not exist. For example, on this site, if you navigate to a URL like <a rel="noopener" target="_blank" class="verbose-url" href="https://rutar.org/does-not-exist">https://rutar.org/does-not-exist</a>, you will be shown a page explaining what happened.</p> <p>For this to happen automatically, we simply need to create a file <code>404.html</code> at the root of our directory, with some content explaining that the page is missing. The 404 is a <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status">HTTP response status code</a>, where the server tells the client that the page you are looking for is missing. Often, the server will default to showing the contents of the <code>/404.html</code> file.</p> <p><a rel="noopener" target="_blank" href="https://raw.githubusercontent.com/alexrutar/webpage-example/master/404.html">Here</a> is an example of this file. This file also has proper naming of links, as discussed above.</p> <h3 id="finishing-up"><a href="#finishing-up"></a>Finishing up</h3> <p>You can view the complete website at <a rel="noopener" target="_blank" class="verbose-url" href="https://example.rutar.org">https://example.rutar.org</a>. The files themselves can be found <a rel="noopener" target="_blank" href="https://github.com/alexrutar/webpage-example">on GitHub</a>. You can ignore the additional files: those will be explained in later sections.</p> <p>Here are some direct links to the files which we have prepared above:</p> <ul> <li><a rel="noopener" target="_blank" class="verbose-url" href="https://raw.githubusercontent.com/alexrutar/webpage-example/master/404.html">404.html</a></li> <li><a rel="noopener" target="_blank" class="verbose-url" href="https://raw.githubusercontent.com/alexrutar/webpage-example/master/index.html">index.html</a></li> <li><a rel="noopener" target="_blank" class="verbose-url" href="https://raw.githubusercontent.com/alexrutar/webpage-example/master/style.css">style.css</a></li> <li><a rel="noopener" target="_blank" class="verbose-url" href="https://raw.githubusercontent.com/alexrutar/webpage-example/master/writing/index.html">writing/index.html</a></li> </ul> <p>In my opinion, the best way to learn more about HTML and CSS is to take a website which you like and use the <strong>View Source</strong> or <strong>Inspect Element</strong> functionality in your browser. The <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/">MDN Web Docs</a> are an incredibly rich resource which contain almost everything you might want to know about web development. Whenever I need to look anything up, that’s where I start.</p> <p>If your browser is Safari on macOS, you can enable some nice development features with the <a rel="noopener" target="_blank" href="https://support.apple.com/en-ca/guide/safari/sfri20948/mac">Safari developer tools</a> option. For example, this allows you to emulate different browsers, and enter a responsive design mode which allows you to manually modify the screen size and view how your webpage will change.</p> <p>Firefox also has similar functionality built into the <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Tools">Firefox developer tools</a>, and the Google Chrome browser in <a rel="noopener" target="_blank" href="https://developer.chrome.com/docs/devtools/">Chrome DevTools</a>.</p> <h2 id="deployment-with-github-and-cloudflare"><a href="#deployment-with-github-and-cloudflare"></a>Deployment with GitHub and Cloudflare</h2> <p>Now that we have created our personal webpage, we want to make it publicly available! In order to do this, we will use <a rel="noopener" target="_blank" href="https://github.com">GitHub</a> to host our files, and then integrate Cloudflare Pages with our GitHub account to take the hosted files and deploy them to our domain.</p> <p>GitHub is a hosting provider for <a rel="noopener" target="_blank" href="https://git-scm.com">git</a>, which is software for tracking and saving changes to a set of files. In fact, I would recommend that you learn how to use git locally, and then integrate it with GitHub. However, the tutorial for setting this up is beyond the scope this article! If you are interested in this, you can find some pointers to get started on the <a rel="noopener" target="_blank" href="https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control">git webpage</a> and on <a rel="noopener" target="_blank" href="https://docs.github.com/en/get-started/quickstart/git-and-github-learning-resources">GitHub</a>.</p> <p>For site deployment, I will cover two main options: using <a rel="noopener" target="_blank" href="https://pages.github.com">GitHub Pages</a>, or using <a rel="noopener" target="_blank" href="https://pages.cloudflare.com">Cloudflare Pages</a>. Both options are free. Generally speaking, Cloudflare Pages is a more comprehensive web hosting solution than GitHub Pages, whereas GitHub Pages is specialized for GitHub repositories. Here are some benefits of using Cloudflare Pages over GitHub Pages:</p> <ol> <li><strong>Easier custom domain setup.</strong> Since Cloudflare is also a domain registrar and DNS provider, they will manage many of the details for you which makes setup substantially simpler.</li> <li><strong>Support for multiple sites.</strong> GitHub Pages restricts you account to 1 site per account, whereas Cloudflare Pages lets you have as many sites as you would like.</li> <li><strong>Repository privacy.</strong> Unless you pay, GitHub Pages requires your repository <a rel="noopener" target="_blank" href="https://docs.github.com/en/get-started/learning-about-github/githubs-products">to be public</a>. It is possible, but somewhat complicated, to work around this requirement. Cloudflare Pages lets you link public or private repositories.</li> </ol> <p>Cloudflare probably also has better server performance, but for a small static webpage, the difference is minimal.</p> <p>On the other hand, the main reason to prefer GitHub Pages over Cloudflare Pages is <strong>decreased complexity</strong>: as we will see below, GitHub Pages is very easy to set up, especially since we already need to host our webpage code on GitHub. The Cloudflare webpage is also rather developer-oriented, so the dashboard can be a bit confusing to use at first.</p> <p>With GitHub Pages, your default URL will be <code>your-username.github.io</code> , where <code>your-username</code> is the username you use to sign up for GitHub Pages. With Cloudflare Pages, your URL will be <code>project-name.pages.dev</code>, where <code>project-name</code> is something you can choose individually for each webpage.</p> <h3 id="creating-the-repository-on-github"><a href="#creating-the-repository-on-github"></a>Creating the repository on GitHub</h3> <p>First, you need to create a new repository. I would recommend that you follow the instructions <a rel="noopener" target="_blank" href="https://docs.github.com/en/get-started/quickstart/hello-world#creating-a-repository=">here</a>. Here are a couple notes:</p> <ol> <li>If you are using Cloudflare Pages, you can name the repository anything you would like (you do not need to choose <code>hello-world</code>). <em>If you are using GitHub Pages, you must name the repository <code>your-username.github.io</code>.</em></li> <li>Optionally, you can choose the <strong>Add README</strong> option, which you can later modify to provide information on your webpage. This file will be used to display information on your repository page.</li> <li>You can also choose to either make your webpage <strong>public</strong> or <strong>private</strong>. A public repository means that anybody can view (but not edit) your GitHub repository, whereas only you—or anybody you give permission—will be able to see the repository if it is private. <em>If you are using GitHub Pages, your repository must be <strong>public</strong>.</em></li> </ol> <p>Once you’ve finished creating the new repository, you need to add files that we created earlier! To do this, click on the <strong>Add File</strong> option, beside the <strong>Code</strong> drop-down menu in green.</p> <ol> <li>If you choose the <strong>Upload files</strong> option, the file will be placed directly into the folder. To upload files to a subfolder, you need to click on the subfolder first and then choose the <strong>Add File</strong> option. Somewhat inconveniently, this requires the folder to already exist.</li> <li>If you choose the <strong>Create new file</strong> option, you can specify the filename directly and copy-paste the contents into the built-in browser. In order to put the file in a subfolder, simply provide a filename containing a backslash. For example, to add the HTML file for the <strong>Writing</strong> tab, enter the name <strong>writing/index.html</strong>.</li> </ol> <p>Once you have added the file(s), click the <strong>Commit new file</strong> option at the bottom. It is simplest to choose the default option (<strong>Commit directly to the —— branch</strong>).</p> <p>When you’ve finished, the repository should look something like <a rel="noopener" target="_blank" href="https://github.com/alexrutar/webpage-example">this</a>.</p> <p>If you have decided to use GitHub Pages, your page should automatically deploy after a few minutes! To see the status of the build, go to the <strong>Actions</strong> tab. You can also see the current status in the <strong>Environments</strong> section of the sidebar on the right hand side.</p> <h3 id="deploying-on-cloudflare"><a href="#deploying-on-cloudflare"></a>Deploying on Cloudflare</h3> <p>In this section, I will recommend that you follow the instructions in the <a rel="noopener" target="_blank" href="https://developers.cloudflare.com/pages/get-started">Cloudflare Pages documentation</a> for complete detail. The general idea is to navigate to the <strong>Pages</strong> tab, select <strong>Create a project</strong>, and follow the instructions. You need to link your GitHub account so that Cloudflare can automatically read the code and update your webpage whenever the code changes.</p> <p>Here are a couple notes:</p> <ol> <li>The <strong>Project name</strong> that you choose will be used to create the default domain. For example, I named my project <code>webpage-example</code> and my default Cloudflare–provided URL is <a rel="noopener" target="_blank" class="verbose-url" href="https://webpage-example.pages.dev">https://webpage-example.pages.dev</a>.</li> <li>Under the <strong>Build command</strong>, simply write <code>exit 0</code>. Leave the <strong>Framework</strong> option and the <strong>Build output directory</strong> as their default values.</li> </ol> <p>Once you’ve finished, click <strong>Save and Deploy</strong>, wait for the build to finish—this will take a few minutes, even though there is nothing to build—and then your webpage will be publicly available!</p> <h3 id="adding-and-modifying-content"><a href="#adding-and-modifying-content"></a>Adding and modifying content</h3> <p>Now, to make edits to your webpage or add new files, simply edit existing files or add new files to the GitHub repository. Once the changes are committed to the repository, Cloudflare Pages (or Github Pages) will automatically update your webpage! Note that there will be a delay—approximately two minutes—from when you commit the change, until the changes are available.</p> <h3 id="a-note-on-git-and-commits"><a href="#a-note-on-git-and-commits"></a>A note on git and commits</h3> <p>Underlying GitHub is the git version control software. Essentially, a commit is a complete copy of the state of all the files that git is tracking, at a given point in time.<label for="in7">7</label><input type="checkbox" id="in7"><small>Internally, git only saves the differences, or this would take a large amount of storage space!</small> Because of this, git saves your entire history, which makes it relatively straightforward to undo changes and view the state of your site in the past. Git is also useful to version control other important files!</p> <p>It is also possible to have multiple versions of your content using <a rel="noopener" target="_blank" href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell">git branches</a>. For example, it is even possible to <a rel="noopener" target="_blank" href="https://rutar.org/writing/previewing-a-development-branch-on-cloudflare-pages/">preview a development branch</a> automatically using Cloudflare Pages.</p> <p>However, doing anything complicated in the GitHub browser is either tedious or impossible. Because of this, I would recommend using git locally, modifying files on your computer, and then synchronizing the changes with your GitHub account.</p> <p>There are also many options to test changes locally before committing them to your GitHub repository. For example, you can use the <a rel="noopener" target="_blank" href="https://docs.python.org/3/library/http.server.html">built-in Python web server</a>, or a <a rel="noopener" target="_blank" href="https://caddyserver.com">more robust option</a>. This way, you can test changes to your site without interfering with your webpage, until you are confident the changes are doing what you expect!</p> <h2 id="further-topics"><a href="#further-topics"></a>Further topics</h2> <h3 id="custom-domains"><a href="#custom-domains"></a>Custom domains</h3> <p>If you followed the instructions above, your URL looks like <code>your-username.github.io</code> or <code>project-name.pages.dev</code>. However, it is also possible to host your website under a custom URL. Beyond the aesthetic benefits of having a distinctive URL, there are a few additional benefits:</p> <ul> <li><strong>Domain name perpetuity.</strong> Domain name registration is regulated by <a rel="noopener" target="_blank" href="https://www.icann.org/registrants">ICANN</a>, and domain name ownership is protected under <a rel="noopener" target="_blank" href="https://www.icann.org/resources/pages/benefits-2013-09-16-en">certain rules</a>. Even if your registrar goes out of business, the domain name is still legally yours. On the other hand, while GitHub and Cloudflare Pages are likely to remain in business for at least a few years, on long timeframes—say multiple decades—there is risk that these sites will cease to exist. Having your own domain guarantees that your site is always available at the same address on the web.</li> <li><strong>Email routing.</strong> It is also possible to use your domain name to route emails. For example, you can contact me at <a rel="noopener" target="_blank" class="verbose-url" href="mailto:[email protected]">[email protected]</a>. Again, this is useful since it is a perpetual email address associated with your name, and you do not need to worry about losing your address if your provider goes out of business.</li> </ul> <p>There are a few parts to setting up a custom domain. First, you need to register a domain name, and then you need to set up <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Domain_Name_System">DNS</a> to associate your domain name with the address where your webpage is hosted.</p> <p>In terms of domain registrars, I would recommend one of the following:</p> <ul> <li><a rel="noopener" target="_blank" href="https://www.cloudflare.com/en-ca/products/registrar/">Cloudflare Registrar</a></li> <li><a rel="noopener" target="_blank" href="https://aws.amazon.com/route53/">AWS Route 53</a></li> <li><a rel="noopener" target="_blank" href="https://gandi.net">Gandi</a></li> </ul> <p>I would definitely be careful with other hosts—certain popular domain registrars engage in <a rel="noopener" target="_blank" href="https://news.ycombinator.com/item?id=24506303">shady practices</a> such as registering domains that you look up on their site so that they can make you pay more money for them! Domain name pricing is also problematic, since many registrars will allow you to register a new domain for a low cost but then charge high renewal fees!</p> <p>In this tutorial we will use Cloudflare. Conveniently, Cloudflare manages DNS and name registration simultaneously, which makes it very easy to set up a new domain. Moreover, if your site is already hosted on Cloudflare, adding the correct DNS records to make your site available can be performed semi-automatically.</p> <h4 id="domain-name-registration"><a href="#domain-name-registration"></a>Domain name registration</h4> <p>In order to register a new domain, carefully follow the instructions in the <a rel="noopener" target="_blank" href="https://developers.cloudflare.com/registrar/get-started/register-domain">documentation</a>. It is important to provide accurate information here, since as discussed before, domain name ownership gives you certain legal rights, and providing correct information is required to maintain this. Note that this information will not be displayed online since Cloudflare uses <a rel="noopener" target="_blank" href="https://developers.cloudflare.com/registrar/why-choose-cloudflare/whois-redaction">WHOIS redaction</a><label for="in8">8</label><input type="checkbox" id="in8"><small><a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/WHOIS">WHOIS</a> is a protocol which enables lookup of domain ownership records. You can see the records for my domain <a rel="noopener" target="_blank" href="https://whois.gandi.net/en/results?search=rutar.org">here</a>.</small>.</p> <h4 id="setting-up-dns"><a href="#setting-up-dns"></a>Setting up DNS</h4> <p>Essentially, <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Domain_Name_System">DNS</a> provides a standardized way to convert web addresses, e.g. <a rel="noopener" target="_blank" class="verbose-url" href="https://rutar.org">https://rutar.org</a>, into <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/IP_address">IP Addresses</a>. As a mildly crude analogy, one can think of DNS as a mapping from house addresses to GPS coordinates.</p> <p>Conveniently, since we are also using Cloudflare for DNS, the domain is automatically linked to the DNS page. To see your DNS record page, navigate to the <strong>Websites</strong> tab, select your domain name, then navigate to the <strong>DNS</strong> tab.</p> <h4 id="adding-a-record-for-our-webpage"><a href="#adding-a-record-for-our-webpage"></a>Adding a record for our webpage</h4> <p>Finally, adding the record for our webpage is straightforward: navigate to the project configuration page (in the <strong>Pages</strong> tab) and select the <strong>Custom domains</strong> tab. Input your custom domain name and select <strong>Continue</strong>. For a bit more detail, see the <a rel="noopener" target="_blank" href="https://developers.cloudflare.com/pages/get-started#adding-a-custom-domain">documentation</a>.</p> <p>Once you’ve done this, you might have to wait a couple minutes for the DNS to propagate (sometimes, you need to refresh your own WIFI connection) and then your site will be available at your domain!</p> <h3 id="adding-a-favicon"><a href="#adding-a-favicon"></a>Adding a favicon</h3> <p>A <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Favicon">favicon</a> is a small image loaded by the browser and displayed on the website tab. There is a lot of different advice on how to appropriately set favicons, and unfortunately a lot of it is very outdated.</p> <p>These days, modern browsers support <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">SVG</a> favicons and this format is the simplest and most robust way to add a favicon to your webpage: it is lightweight and scales properly. There are lots of tools available to create SVG favicons—I used <a rel="noopener" target="_blank" href="https://formito.com/tools/favicon">this site</a>. You can also just download an icon from a place like <a rel="noopener" target="_blank" href="https://fontawesome.com/v5.15/icons?d=gallery&amp;p=2&amp;m=free">font awesome</a>. If you want to create a custom icon, you can use an <a rel="noopener" target="_blank" href="https://yqnn.github.io/svg-path-editor/">online SVG editor</a>.</p> <p>To add the favicon to our webpage, first copy the icon file, say <code>icon.svg</code>, to the root of your project directory. Then add the following line to the <code>&lt;head&gt;</code> of each page on your site:</p> <pre data-lang="html" class="language-html z-code"><code class="language-html" data-lang="html"><span class="z-text z-html z-basic"><span class="z-meta z-tag z-inline z-any z-html"><span class="z-punctuation z-definition z-tag z-begin z-html">&lt;</span><span class="z-entity z-name z-tag z-inline z-any z-html">link</span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">rel</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>icon<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">href</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>/icon.svg<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span> <span class="z-meta z-attribute-with-value z-html"><span class="z-entity z-other z-attribute-name z-html">type</span><span class="z-punctuation z-separator z-key-value z-html">=</span><span class="z-string z-quoted z-double z-html"><span class="z-punctuation z-definition z-string z-begin z-html">&quot;</span>image/svg+xml<span class="z-punctuation z-definition z-string z-end z-html">&quot;</span></span></span><span class="z-punctuation z-definition z-tag z-end z-html">&gt;</span></span> </span></code></pre> <p>This tells the browser that there’s an icon located at <code>/icon.svg</code>, and that it’s an SVG file.</p> <p>If you wish to support more devices, I’d recommend that you read this <a rel="noopener" target="_blank" href="https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs">favicon article</a>. The next most important file to create would be a <code>favicon.ico</code> file: this will display the favicon even on outdated browsers. Image favicons are somewhat more flexible than SVG favicons, since it is easy to convert most visual files to an ICO file.<label for="in9">9</label><input type="checkbox" id="in9"><small>For example, you might consider <a rel="noopener" target="_blank" href="https://www.websiteplanet.com/webtools/favicon-generator/">this site</a> to generate favicon files from existing images. Thanks to a reader for this nice suggestion!</small></p> <h3 id="site-security"><a href="#site-security"></a>Site security</h3> <p>In order to prevent malicious parties from hijacking your web traffic and impersonating your site, it is important that all HTTP connections are encrypted with <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a>. There are <a rel="noopener" target="_blank" href="https://doesmysiteneedhttps.com/">many opinions</a> on why HTTPS is important.</p> <p>With Cloudflare Pages and GitHub Pages, this is automatically managed on your behalf: if you navigate to your site, you should a small lock symbol somewhere near the address bar. If you click on this lock symbol, your browser will show some information about the connection.</p> <p>However, beyond serving your content over HTTPS, there are some other site security features you can implement with a minimal amount of effort.</p> <h4 id="basic-security-headers"><a href="#basic-security-headers"></a>Basic security headers</h4> <p><a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers">HTTP headers</a> are used to to pass additional (often meta) information with the body of the HTTP request. One usage of HTTP headers is to make guarantees about the content and prevent your site from being misused, or the content from being hijacked.</p> <p>In order to add headers to your site hosted on Cloudflare, you simply need to add a file called <code>_headers</code> to your project directory. The syntax for the headers document is of the form</p> <pre class="z-code"><code><span class="z-text z-plain">[url] </span><span class="z-text z-plain"> [name]: [value] </span></code></pre> <p>You can read more about the <code>_headers</code> file on the <a rel="noopener" target="_blank" href="https://developers.cloudflare.com/pages/platform/headers">Cloudflare documentation</a>.</p> <p>For now, populate the file with the following contents:</p> <pre class="z-code"><code><span class="z-text z-plain">/* </span><span class="z-text z-plain"> X-Frame-Options: DENY </span><span class="z-text z-plain"> X-Content-Type-Options: nosniff </span><span class="z-text z-plain"> Referrer-Policy: same-origin </span></code></pre> <p>This adds the following headers to any HTTP request set to a URL matching the <code>/*</code> pattern (that is, every URL on your site):</p> <ul> <li><a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options">X-Frame-Options</a>: This prevents your site from being embedded on other websites.</li> <li><a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options">X-Content-Type-Options</a>: Don’t let the browser guess what type of content you are linking. This requires that you properly label the content type in linked content, e.g. <code>type="text/css"</code> from the <a href="https://rutar.org/writing/how-to-build-a-personal-webpage-from-scratch/#styling-the-page">CSS link</a>.</li> <li><a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy">Referrer-Policy</a>: Control what information is passed along when a visitor clicks on links in your site. The value <code>same-origin</code> means that, if a visitor to your webpage navigates clicks on a URL that is not on your site, no information is passed along.</li> </ul> <h4 id="content-security-policy"><a href="#content-security-policy"></a>Content security policy</h4> <p>A <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">content security policy (CSP)</a> is a way for your site to declare what type of content it is serving. This can be used to guarantee that the content on your site is sent exactly as you intended, along with some other features.</p> <p>For our example site, since all the content is served from the same domain (<code>self</code>), and the only non-HTML content we are serving are CSS pages and images (for example, the favicon), our CSP can be pretty restrictive. Here’s what we will use:</p> <pre class="z-code"><code><span class="z-text z-plain">Content-Security-Policy: default-src &#39;none&#39;; img-src &#39;self&#39;; style-src &#39;self&#39;; upgrade-insecure-requests; form-action &#39;none&#39;; base-uri &#39;none&#39;; frame-ancestors &#39;none&#39; </span></code></pre> <p>Here is an explanation of the various components.</p> <ul> <li><code>default-src 'none'</code>: by default, we are serving no content other than HTML.</li> <li><code>img-src 'self'</code> and <code>style-src 'self'</code>: include CSS and images, but only from the same domain.</li> <li><code>upgrade-insecure-requests</code>: redirect <code>http://</code> to <code>https://</code>.</li> <li><code>form-action 'none'</code>: do not send HTTP forms anywhere.</li> <li><code>base-uri: none</code>: the document base URL is just <code>/</code>.</li> <li><code>frame-ancestors 'none'</code>: similar to the <code>X-Frame-Options: DENY</code> from before, since it prevents your site from being embedded elsewhere.</li> </ul> <p>To see more details on creating a content security policy, the <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#writing_a_policy">MDN page</a> has a nice explanation. Note that if you want to include JavaScript (or other content), especially content loaded from a different domain, modifying your CSP will require a bit more work.</p> <h4 id="hsts-preload"><a href="#hsts-preload"></a>HSTS Preload</h4> <p>Essentially, <a rel="noopener" target="_blank" href="https://hstspreload.org">HTTP Strict Transport Security (HSTS)</a> provides a list of sites that are hard-coded to be using HTTPS. This list is used by your browser to guarantee to prevent connecting to a site on the list without using HTTPS. If you are committed to using HTTPS, you can register your site for HSTS Preload.</p> <p>In order to implement this, you need to register your site at the URL <a rel="noopener" target="_blank" class="verbose-url" href="https://hstspreload.org">https://hstspreload.org</a>. Most of the steps are already done: the only thing that remains to be done is to add the <code>Strict-Transport-Security</code> header as follows:</p> <pre class="z-code"><code><span class="z-text z-plain">Strict-Transport-Security: max-age=31536000; preload; includeSubDomains </span></code></pre> <p>You can also view the status of your domain by using the search bar at the top of the page.</p> <p>Another option is to enable this on Cloudflare directly: follow the instructions <a rel="noopener" target="_blank" href="https://developers.cloudflare.com/ssl/edge-certificates/additional-options/http-strict-transport-security#enable-hsts">here</a>. You must set the <strong>Max Age</strong> setting to 12 months, and I would also recommend selecting all the other options.</p> <h4 id="complete-header-file"><a href="#complete-header-file"></a>Complete header file</h4> <p>Here are the contents of the <code>_headers</code> file, assuming you have implemented all of the recommendations above.</p> <pre class="z-code"><code><span class="z-text z-plain">/* </span><span class="z-text z-plain"> X-Frame-Options: DENY </span><span class="z-text z-plain"> X-Content-Type-Options: nosniff </span><span class="z-text z-plain"> Referrer-Policy: same-origin </span><span class="z-text z-plain"> Content-Security-Policy: default-src &#39;none&#39;; img-src &#39;self&#39;; style-src &#39;self&#39;; upgrade-insecure-requests; form-action &#39;none&#39;; base-uri &#39;none&#39;; frame-ancestors &#39;none&#39; </span><span class="z-text z-plain"> Strict-Transport-Security: max-age=31536000; preload; includeSubDomains </span></code></pre> <p>You can also see the file <a rel="noopener" target="_blank" href="https://raw.githubusercontent.com/alexrutar/webpage-example/master/_headers">here</a>.</p> <p>There are many ways to verify that the headers are loaded properly on your site. The <a rel="noopener" target="_blank" href="https://observatory.mozilla.org/analyze/rutar.org">Mozilla observatory</a> page is a nice resource, along with <a rel="noopener" target="_blank" class="verbose-url" href="https://webbkoll.dataskydd.net/en/">https://webbkoll.dataskydd.net/en/</a> for giving more detailed information and recommendations.</p> Vim Session Management: An Introduction to Fish 2022-02-01T00:00:00+00:00 2022-02-01T00:00:00+00:00 https://rutar.org/writing/vim-session-management-an-introduction-to-fish/ <h2 id="objectives"><a href="#objectives"></a>Objectives</h2> <p><a rel="noopener" target="_blank" href="https://fishshell.com">Fish shell</a> is a user-friendly command line shell and scripting langauge. However, the project is relatively new, so it can be somewhat challenging to find concise information on how to use the scripting language.</p> <p>In this article, we will develop a basic session management tool for (Neo)Vim. The intention of this tool is to be a wrapper around Tim Pope’s <a rel="noopener" target="_blank" href="https://github.com/tpope/vim-obsession">obsession.vim</a> plugin, with the following features:</p> <ol> <li><a href="https://rutar.org/writing/vim-session-management-an-introduction-to-fish/#session-initialization-and-management">Easy session initialization</a>: list sessions and open them</li> <li><a href="https://rutar.org/writing/vim-session-management-an-introduction-to-fish/#active-session-management">Active session management</a>: we only want to allow a single instance of vim to be using a session file</li> <li><a href="https://rutar.org/writing/vim-session-management-an-introduction-to-fish/#autocompletions">Autocompletions</a>: get relevant results when you hit <kbd><code>TAB</code></kbd></li> </ol> <p>Perhaps you simply find the session management tool useful: you can find the program in the <a rel="noopener" target="_blank" href="https://github.com/alexrutar/vs">Git repository</a>. This implements all the features below, along with a couple extra useful commands, completions, and help messages.</p> <h3 id="prerequisites"><a href="#prerequisites"></a>Prerequisites</h3> <p>I will assume that you have the tools <a rel="noopener" target="_blank" href="https://github.com/sharkdp/fd">fd</a> and <a rel="noopener" target="_blank" href="https://github.com/junegunn/fzf">fzf</a> installed and accessible in your shell. If you don’t, you can simply omit the components that use <code>fzf</code>, and rewrite the parts using <code>fd</code> to use <code>find</code> instead. I will also assume you know a bit about (Neo)Vim, including plugin installation.</p> <h2 id="session-initialization-and-management"><a href="#session-initialization-and-management"></a>Session initialization and management</h2> <p>In this section, we will write the core functionality of our program: save and open session files.</p> <p>First, let’s create a global variable to represent where we want to save the session files. Add the line</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">x</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">VS_SESSION_DIR</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>~/.local/share/vim/sessions<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>to your <code>config.fish</code>, or wherever you prefer to define environment variables. You can set the folder to be anything you want. Now <code>exec fish</code> to load this variable. To ensure that this variable is loaded, you can run</p> <pre><code><kbd>envs | grep ^VS_SESSION_DIR</kbd></code></pre> <p>and check that there is a match. The command <code>env</code> prints out all currently defined environment variables—we just search for the line that starts with <code>VS_SESSION_DIR</code>.</p> <h3 id="a-wrapper-for-obsession-vim"><a href="#a-wrapper-for-obsession-vim"></a>A wrapper for Obsession.vim</h3> <p><a rel="noopener" target="_blank" href="https://github.com/tpope/vim-obsession">Obsession.vim</a> is itself a wrapper around the vim command <code>:mksession</code>, which creates a session file and saves the current state of vim (e.g. tabs, windows, layout, etc.) to that file. If you have a session file <code>session.vim</code>, you can restart it with <code>vim -S session.vim</code>. Install this plugin in your <code>vimrc</code> and also add the function definition</p> <pre data-lang="vim" class="language-vim z-code"><code class="language-vim" data-lang="vim"><span class="z-source z-viml"><span class="z-support z-function z-viml">command</span> <span class="z-support z-type z-viml">-nargs=</span><span class="z-constant z-numeric z-integer">1</span> VSave Obsess $VS_SESSION_DIR/<span class="z-support z-type z-viml">&lt;args&gt;</span><span class="z-storage z-function z-viml">.</span>vim </span></code></pre> <p>This defines a command <code>:VSave</code>, which takes exactly one argument which is the name of the session file (with no extension). To begin a new saved session, run <code>:VSave my/session</code> from an active vim instance. To terminate, run <code>qa</code>: note that closing files individually will modify the session file so those files will remain closed on restart.</p> <h3 id="starting-up-existing-sessions"><a href="#starting-up-existing-sessions"></a>Starting up existing sessions</h3> <p>Fish functions are defined in the folder <code>~/.config/fish/functions</code> and corresponding completions in <code>~/.config/fish/completions</code>. Let’s write the command to start up new sessions, which we will invoke with <code>vs</code>. We want to accept the session name as an argument, and then check if the session file exists: if it does, open it; if not, terminate with a nice error message. In the file <code>~/.config/fish/functions/vs.fish</code>, we define a function as follows:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">argument</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">session_name</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">sessionfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span><span class="z-meta z-string z-unquoted z-fish">/session_name.vim</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">S</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Could not find session &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span>&#39;<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>To invoke, just run <code>vs &lt;session name&gt;</code>, and the corresponding session will be started!</p> <p>The <code>--argument session_name</code> automatically assigns the first argument passed to <code>vs</code> to the variable <code>session_name</code>. Note that the variable will be empty if there are no arguments passed to <code>vs</code>. Any other arguments are simply ignored. In shell scripting languages, conditionals often execute based on the return code of a command. In this case, the command <code>test -f</code> returns 0 if <code>$sessionfile</code> exists, and returns something else otherwise. We redirect the output of <code>echo</code> to STDERR with <code>&gt;&amp;2</code>, and then return with the default error code 1.</p> <p>Note that you can define multiple functions in a single file <code>myfunc.fish</code>, but fish only knows to load those functions on request if there is a matching filename. You should only define helper functions for the main function in the file, since they will not be loaded until the main function is called.</p> <p>You can also edit functions in the current interactive shell with <code>funced &lt;function name&gt;</code>, and save the functions to the directory with <code>funcsave &lt;function name&gt;</code>. However, doing this will remove any helper functions you have defined in the file! This is also an easy way to introspect fish functions which you may have not defined yourself.</p> <p>If you edit a function file, sometimes you need to reload your shell for proper execution. The easiest way to do this is to <code>exec fish</code>.</p> <h3 id="a-basic-subcommand-session-listing"><a href="#a-basic-subcommand-session-listing"></a>A basic subcommand: session listing</h3> <p>For convenience, it would also be nice to be able to get a list of the existing session names. This utility will also be necessary later, when we provide autocompletion. Now, since we want multiple behaviours, we will invoke the desired behaviour with two subcommands: we will invoke the previous function with <code>open</code>, and the new listing function with <code>list</code>. First, we define a helper function to list sessions. Using <code>fd</code>, we can quickly get a list of candidate files:</p> <pre><code><kbd>fd -e vim --base-directory $VS_SESSION_DIR</kbd></code></pre> <p>However, we only want the name of the session and not the extension <code>.vim</code>. The easiest way to do this is to use <code>--exec echo {.}</code>: <code>{.}</code> is replaced with the filename with no extension. This also handles the case where the filename has multiple periods, unlike something more direct such as <code>cut -d "." -f 1</code>. We also want to sort the output, since <code>fd</code> is multithreaded by default when called with <code>--exec</code> and the order can change each time (which could be confusing).</p> <p>Wrapping this in a function, we get</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_list_sessions</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">fd</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">e</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">base-directory</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">exec</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-braces z-literal z-non-empty z-fish">{</span><span class="z-meta z-braces z-literal z-non-empty z-fish">.</span><span class="z-meta z-braces z-literal z-non-empty z-fish">}</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-pipe z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-pipe z-fish">|</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">sort</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>We can add this as a subcommand to our original function:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">argument</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">command</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">session_name</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-block z-switch z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">switch</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>command</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">case</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">open</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">sessionfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span><span class="z-meta z-string z-unquoted z-fish">/session_name.vim</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">S</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Could not find session &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span>&#39;<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">case</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">list</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_list_sessions</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>Now, open session files with <code>vs open session/name</code> and list possibilities with <code>vs list</code>.</p> <h3 id="fancy-session-selection-with-fzf"><a href="#fancy-session-selection-with-fzf"></a>Fancy session selection with fzf</h3> <p>For convenience, let’s also write an interactive file chooser using <a rel="noopener" target="_blank" href="https://github.com/junegunn/fzf">fzf</a> This command reads input from STDIN and opens up an interactive browser which allows selection. Upon choosing an option, the corresponding line is sent to STDOUT. This variable is captured using fish parameter expansion <code>(...)</code> and saved in the variable <code>fzf_session</code>. Note that if <code>fzf</code> is terminated early using <kbd><code>Ctrl+C</code></kbd>, the variable <code>$fzf_session</code> will not be saved, so we also need to check that it is non-empty.</p> <p>Add the following at the beginning of the indentation block directly below <code>case open</code>:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-operator z-word z-not z-fish"><span class="z-meta z-string z-unquoted z-fish">not</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">fzf_session</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-parens z-command-substitution z-fish"><span class="z-punctuation z-section z-parens z-begin z-fish">(</span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__v_list_sessions</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-pipe z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-pipe z-fish">|</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">fzf</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">height</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">40%</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">border</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">tac</span></span></span><span class="z-punctuation z-section z-parens z-end z-fish">)</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>fzf_session</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">session_name</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>fzf_session</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">0</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>Essentially, when <code>vs open</code> is called with no session name, an interactive prompt opens and allows you to search the available sessions.</p> <p>In fish, quotes are usually not required since variables are passed ‘as atoms’ rather than being expanded and separated on whitespace (as is the case with bash). One of the few exceptions to this rule is <code>test -n "$var"</code>, which checks that <code>var</code> is defined and non-empty. In this situation, you must always quote the variable since if var is not defined, then <code>$var</code> will expand to an argument list of length 0, essentially calling <code>test -n</code> instead of the desired <code>test -n ""</code>.</p> <h2 id="active-session-management"><a href="#active-session-management"></a>Active session management</h2> <p>Now let’s add active session management. If one shell instance has a session file open, we want to prevent another instance from opening up the same session file: multiple writes to the session file could corrupt the file! We will achieve this with <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/File_locking">file locking</a>. However, a bit of care needs to be taken: we need to handle termination of the script while it is running. The easiest way to do this to use <a rel="noopener" target="_blank" href="https://fishshell.com/docs/current/cmds/function.html">event handlers</a>.</p> <p>When a function is created, it can be registered as an event handler for certain events. We care about three events: when the function receives the signal <code>SIGINT</code> or <code>SIGHUM</code>, which indicates interruption of the script, or when the shell itself terminates.</p> <h3 id="basic-event-handler-example"><a href="#basic-event-handler-example"></a>Basic event handler example</h3> <p>Consider the following function:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">example</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__example_cleanup</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-signal</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">INT</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-signal</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">HUP</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-event</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">fish_exit</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">functions</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">e</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">__example_cleanup</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Cleaning up!<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">read</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">p</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>echo &quot;Press ENTER to continue &quot;<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Done!<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__example_cleanup</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>If you run the script normally, following the prompt, the function simply prints</p> <pre><code><kbd>example</kbd> Press ENTER to continue [ENTER] Done! Cleaning up!</code></pre> <p>to your terminal. However, suppose instead of pressing <kbd><code>ENTER</code></kbd>, you hit <kbd><code>Ctrl+C</code></kbd> to terminate. Then the <code>__example_cleanup</code> event runs immediately, and the function will print</p> <pre><code><kbd>example</kbd> Press ENTER to continue [Ctrl-C] Cleaning up!</code></pre> <p>Even though the function never completed, the cleanup function still fires.</p> <p>The handler <code>--on-event fish_exit</code> also catches the case where you, say, close the entire terminal window while the function is running. Note that we must delete the function <code>__example_cleanup</code> when we execute it, with <code>functions -e</code>. Otherwise, <code>__example_cleanup</code> will continue to live in our interactive shell and will fire even if we run <kbd><code>Ctrl+C</code></kbd> during the execution of a different program.</p> <h3 id="incorporating-file-locking"><a href="#incorporating-file-locking"></a>Incorporating file locking</h3> <p>Our idea is now the following: when we first start up our session, we check for the existence of lock files. If one does not exist, create it, and start up the session; otherwise, terminate with an error, For convenience, since the session files are saved as <code>&lt;session name&gt;.vim</code>, let’s save the lock files as <code>&lt;session name&gt;.lock</code>. In order to avoid race conditions, we can create the lock file and test its existence simultaneously using <code>mkdir</code>:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">mkdir</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdin z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-read z-fish">&lt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">session</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">name</span></span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">.lock</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-std-write z-truncate z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">&amp;</span></span><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">/dev/null</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Normal execution...<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">0</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Lock file exists!<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>This works since <code>mkdir</code> returns error code 1 if the directory it is trying to create already exists. We also supress the <code>mkdir</code> error message, since we want to send a more meaningful one to the user ourselves.</p> <p>We also need to clean up the lockfile on exist using the event handler from the previous section. All together, our code now looks like this</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_cleanup</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">inherit-variable</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">lockfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-signal</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">INT</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-signal</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">HUP</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-event</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">fish_exit</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">functions</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">e</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_cleanup</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">rmdir</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>lockfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">mkdir</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>lockfile</span></span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-std-write z-truncate z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">&amp;</span></span><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">/dev/null</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">S</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_cleanup</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Session &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>vs_fname</span></span>&#39; already running!<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">...</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></code></pre> <p>In principle, we might have to worry about multiple instances of the same event handler <code>__vs_cleanup</code>. However, as of the time of writing this article, fish <a rel="noopener" target="_blank" href="https://github.com/fish-shell/fish-shell/issues/238">does not support background functions</a>, so we only need to worry about our function running in multiple shells, in which case we do not need have this issue.</p> <h3 id="a-note-on-trap"><a href="#a-note-on-trap"></a>A note on trap</h3> <p>Fish comes with a function <code>trap</code>, which is just a wrapper around the event handler method explained above. At its core, the <a rel="noopener" target="_blank" href="https://github.com/fish-shell/fish-shell/blob/master/share/functions/trap.fish">implementation of trap</a> converts <code>EXIT</code> into <code>--on-event fish_exit</code> and all other signals into <code>--on-signal &lt;signal name&gt;</code>. You can call <code>trap</code> directly with the cleanup function (with no handlers attached), like</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">trap</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">__example_cleanup</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">INT</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">HUP</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">EXIT</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>Note that, in this situation, the event handlers are not automatically deleted. To do this, you need to run</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">trap</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">INT</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">HUP</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">EXIT</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>to reset the traps.</p> <p>To debug issues with handlers persisting longer than you expect, you can get a list of all active handlers with</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">functions</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">handlers</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <h2 id="finishing-up"><a href="#finishing-up"></a>Finishing up</h2> <h3 id="starting-new-sessions"><a href="#starting-new-sessions"></a>Starting new sessions</h3> <p>It would also be nice to start new sessions with a terminal in the given directory. We can tell vim to execute some commands using the <code>+&lt;command&gt;</code> syntax: such arguments passed to vim will be executed in order as the vim session is started. For example, if you run</p> <pre><code><kbd>vim +term</kbd></code></pre> <p>you will get a vim terminal pane in the current directory. Since we already wrote a function <code>VSave</code> earlier, it’s a simple matter to call this function as well:</p> <pre><code><kbd>vim &quot;+silent VSave &lt;session_name&gt;&quot; +term</kbd></code></pre> <p>The <code>silent</code> command executes the next command without printing anything into the Vim pane. We also do a quick check that there is not an existing session with the provided name.</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">sessionfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span><span class="z-meta z-string z-unquoted z-fish">/</span><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span></span><span class="z-meta z-string z-unquoted z-fish">.vim</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Cannot overwrite existing session &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span>&#39;<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">mkdir</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">p</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-parens z-command-substitution z-fish"><span class="z-punctuation z-section z-parens z-begin z-fish">(</span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">dirname</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-punctuation z-section z-parens z-end z-fish">)</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-control z-fish"><span class="z-keyword z-operator z-control z-double-ampersand z-fish">&amp;&amp;</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>+silent VSave <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">+term</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>Now, running <code>vs init &lt;session_name&gt;</code> will create a new session with name <code>&lt;session_name&gt;</code> and start out with a terminal in the directory where the command was called.</p> <h3 id="the-entire-function"><a href="#the-entire-function"></a>The entire function</h3> <p>It remains to add a case where an invalid command is given, and to print out a short error message. After doing this, our file <code>vs.fish</code> now has the following contents:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">argument</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">command</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">session_name</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">new_session_name</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__v_list_sessions</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">fd</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">e</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">base-directory</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">exec</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-braces z-literal z-non-empty z-fish">{</span><span class="z-meta z-braces z-literal z-non-empty z-fish">.</span><span class="z-meta z-braces z-literal z-non-empty z-fish">}</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-pipe z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-pipe z-fish">|</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">sort</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">q</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">VS_SESSION_DIR</span></span><span class="z-meta z-function-call z-operator z-control z-fish"><span class="z-keyword z-operator z-control z-fish">;</span></span> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-operator z-word z-fish"><span class="z-meta z-string z-unquoted z-fish">or</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">VS_SESSION_DIR</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>~/.local/share/nvim/sessions<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-block z-switch z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">switch</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>command</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">case</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">open</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-operator z-word z-not z-fish"><span class="z-meta z-string z-unquoted z-fish">not</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">fzf_session</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-parens z-command-substitution z-fish"><span class="z-punctuation z-section z-parens z-begin z-fish">(</span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_list_sessions</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-pipe z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-pipe z-fish">|</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">fzf</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">height</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">40%</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">border</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">tac</span></span></span><span class="z-punctuation z-section z-parens z-end z-fish">)</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>fzf_session</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">session_name</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>fzf_session</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">0</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">sessionfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span><span class="z-meta z-string z-unquoted z-fish">/</span><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span></span><span class="z-meta z-string z-unquoted z-fish">.vim</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">lockfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span><span class="z-meta z-string z-unquoted z-fish">/</span><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span></span><span class="z-meta z-string z-unquoted z-fish">.lock</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">function</span></span> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-entity z-name z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_cleanup</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">inherit-variable</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">lockfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-signal</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">INT</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-signal</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">HUP</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">on-event</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">fish_exit</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">functions</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">e</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_cleanup</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">rmdir</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>lockfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-function z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">mkdir</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>lockfile</span></span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-std-write z-truncate z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">&amp;</span></span><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">/dev/null</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">S</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_cleanup</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Session &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span>&#39; already running!<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Could not find session &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span>&#39;<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">case</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">list</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">__vs_list_sessions</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">case</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">init</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">sessionfile</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>VS_SESSION_DIR</span></span></span><span class="z-meta z-string z-unquoted z-fish">/</span><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span></span><span class="z-meta z-string z-unquoted z-fish">.vim</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-block z-if z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">if</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">test</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Cannot overwrite existing session &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span>&#39;<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">else</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">mkdir</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">p</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-parens z-command-substitution z-fish"><span class="z-punctuation z-section z-parens z-begin z-fish">(</span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">dirname</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>sessionfile</span></span></span></span><span class="z-punctuation z-section z-parens z-end z-fish">)</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-control z-fish"><span class="z-keyword z-operator z-control z-double-ampersand z-fish">&amp;&amp;</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">vim</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>+silent VSave <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>session_name</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">+term</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"><span class="z-meta z-block z-if z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">case</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>*<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">echo</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>Invalid command option &#39;<span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>argv</span><span class="z-meta z-brackets z-index-expansion z-fish"><span class="z-punctuation z-section z-brackets z-begin z-fish">[</span><span class="z-constant z-numeric z-fish">1</span><span class="z-punctuation z-section z-brackets z-end z-fish">]</span></span></span>&#39;<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-redirection z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-redirect z-write z-truncate z-fish">&gt;</span></span><span class="z-meta z-function-call z-operator z-redirection z-fish"><span class="z-keyword z-operator z-redirect z-dereference z-fish">&amp;</span></span><span class="z-meta z-function-call z-operator z-redirection z-file-descriptor z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-file-descriptor z-fish">2</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">return</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-numeric z-fish"><span class="z-meta z-string z-unquoted z-fish"><span class="z-constant z-numeric z-fish">1</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-block z-switch z-fish"> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></span><span class="z-source z-shell z-fish"><span class="z-meta z-block z-function z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-control z-conditional z-fish"><span class="z-meta z-string z-unquoted z-fish">end</span></span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>Here are some other feature ideas:</p> <ul> <li>custom <code>rename</code> and <code>delete</code> commands</li> <li>nicer file listing with <code>tree</code> (hint: use <code>tree --fromfile .</code> to format input from STDIN, and <code>isatty</code> to only format when the output is a terminal)</li> <li>add some nice help messages</li> </ul> <h3 id="autocompletions"><a href="#autocompletions"></a>Autocompletions</h3> <p>Finally, it would be nice to have some autocompletions for our script. Completion files are stored in a file with the same name as the function file, except in the <code>~/.config/fish/completions</code> directory.</p> <p>We want basic descriptions for the commands when we hit <kbd><code>TAB</code></kbd>, and we also want autocompletion for the session name when we call <code>vs open</code>. Fish completion files are also just regular lists of functions, except they are loaded when autocompletion for a certain function is requested. You can read the fish <a rel="noopener" target="_blank" href="https://fishshell.com/docs/current/completions.html">docs about completions</a> if you would like.</p> <p>We begin by disabling file completion on the base command, which is enabled by default. This is done with the command</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>This way, when we hit tab, we are not suggested offered files in the current directory in the completion list. Now, we need to add our subcommands. This is done as follows: to add the completion option <code>open</code>, we use</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">open</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">d</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>Open the session file<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>However, this has a problem since now fish will suggest <code>open</code> as a valid argument at any time, when it should only be valid at the beginning. In order to fix this, we first introduce a list of all our valid command names with</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs_subcommands</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">open</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">list</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>and then use the <code>-n</code> flag for <code>complete</code> to only complete in the case that we have not yet seen any subcommands. We can also include an option for <code>vs list</code>.</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">open</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>not __fish_seen_subcommand_from <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>vs_subcommands</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">d</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>Open the session file<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">list</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>not __fish_seen_subcommand_from <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>vs_subcommands</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">d</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>List available session files<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">init</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>not __fish_seen_subcommand_from <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>vs_subcommands</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">d</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>Start up a new session<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>Finally, when we have seen the subcommand <code>vs open</code>, we want to provide the valid list of options. Here, the <code>vs list</code> command comes in handy:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>(vs list)<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>__fish_seen_subcommand_from open<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>(vs list)<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>As a whole, the contents of the completion file can be found <a rel="noopener" target="_blank" href="https://github.com/alexrutar/vs/blob/master/completions/vs.fish">in the Git repository</a>. As a whole, the completion file <code>~/.config/fish/completions/vs.fish</code> looks like</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">l</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs_subcommands</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">init</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">list</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">open</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">f</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">open</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>not __fish_seen_subcommand_from <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>vs_subcommands</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">d</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>Open the session file<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">list</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>not __fish_seen_subcommand_from <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>vs_subcommands</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">d</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>List available session files<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">init</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>not __fish_seen_subcommand_from <span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>vs_subcommands</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">d</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-single z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&#39;</span>Start up a new session<span class="z-punctuation z-definition z-string z-end z-fish">&#39;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">complete</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">c</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">vs</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>(vs list)<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-constant z-character z-escape z-newline z-fish">\ </span></span><span class="z-source z-shell z-fish"> <span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">n</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>__fish_seen_subcommand_from open<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">a</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>(vs list)<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>Now hitting <code>vs</code> <kbd><code>SPACE</code></kbd> <kbd><code>TAB</code></kbd> prompts with two options: <code>open</code>, or <code>list</code>, and hitting <code>vs open</code> <kbd><code>SPACE</code></kbd> <kbd><code>TAB</code></kbd> prompts with the possible session names.</p> Managing Python Versions with Pyenv 2022-01-20T00:00:00+00:00 2022-01-20T00:00:00+00:00 https://rutar.org/writing/managing-python-versions-with-pyenv/ <p>The <a rel="noopener" target="_blank" href="https://github.com/pyenv/pyenv">pyenv</a> command line utility is convenient tool for managing python versions.</p> <h2 id="installation"><a href="#installation"></a>Installation</h2> <p>It’s best to follow the above link for general installation instructions. Otherwise, I will assume you are using macOS with Fish Shell.</p> <p>First install <code>pyenv</code> and <code>pyenv-virtualenv</code> with brew:</p> <pre><code><kbd>brew install pyenv pyenv-virtualenv</kbd></code></pre> <p>You also want to add some lines to your <code>config.fish</code>:</p> <pre data-lang="fish" class="language-fish z-code"><code class="language-fish" data-lang="fish"><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">set</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-short z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-short z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-string z-unquoted z-fish">Ux</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">PYENV_ROOT</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span><span class="z-meta z-variable-expansion z-fish"><span class="z-variable z-other z-fish"><span class="z-punctuation z-definition z-variable z-fish">$</span>HOME</span></span><span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span><span class="z-meta z-string z-unquoted z-fish">/.pyenv</span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">status</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">is-login</span></span><span class="z-meta z-function-call z-operator z-control z-fish"><span class="z-keyword z-operator z-control z-fish">;</span></span> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-operator z-word z-fish"><span class="z-meta z-string z-unquoted z-fish">and</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">pyenv</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">init</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-option z-long z-fish"><span class="z-variable z-parameter z-fish"><span class="z-punctuation z-definition z-option z-long z-begin z-fish"><span class="z-meta z-string z-unquoted z-fish">--</span></span><span class="z-meta z-string z-unquoted z-fish">path</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-pipe z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-pipe z-fish">|</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">source</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">status</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">is-interactive</span></span><span class="z-meta z-function-call z-operator z-control z-fish"><span class="z-keyword z-operator z-control z-fish">;</span></span> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-operator z-word z-fish"><span class="z-meta z-string z-unquoted z-fish">and</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">pyenv</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">init</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-pipe z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-pipe z-fish">|</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">source</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">status</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">is-interactive</span></span><span class="z-meta z-function-call z-operator z-control z-fish"><span class="z-keyword z-operator z-control z-fish">;</span></span> <span class="z-meta z-function-call z-name z-fish"><span class="z-keyword z-operator z-word z-fish"><span class="z-meta z-string z-unquoted z-fish">and</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">pyenv</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">virtualenv-init</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">-</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-operator z-pipe z-stdout z-implicit z-fish"><span class="z-keyword z-operator z-pipe z-fish">|</span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">source</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span><span class="z-source z-shell z-fish"><span class="z-meta z-function-call z-name z-fish"><span class="z-variable z-function z-fish"><span class="z-meta z-string z-unquoted z-fish">alias</span></span></span><span class="z-meta z-function-call z-fish"> </span><span class="z-meta z-function-call z-parameter z-argument z-path z-fish"><span class="z-meta z-string z-unquoted z-fish">brew=</span><span class="z-string z-quoted z-double z-fish"><span class="z-punctuation z-definition z-string z-begin z-fish">&quot;</span>env PATH=(string replace (pyenv root)/shims &#39;&#39; <span class="z-constant z-character z-escape z-fish">\&quot;</span><span class="z-constant z-character z-escape z-fish">\$</span>PATH<span class="z-constant z-character z-escape z-fish">\&quot;</span>) brew<span class="z-punctuation z-definition z-string z-end z-fish">&quot;</span></span></span><span class="z-meta z-function-call z-operator z-control z-fish"> </span></span></code></pre> <p>You can set <code>PYENV_ROOT</code> to be any location you would like. The remaining commands are used to initialize <code>pyenv</code> and add the corresponding python versions to your <code>PATH</code>.</p> <h2 id="startup"><a href="#startup"></a>Startup</h2> <p>The first thing to do is to set your preferred global python version:</p> <pre><code><kbd>pyenv global 2.7.18 3.10.1</kbd></code></pre> <p>sets the <code>python2</code> version to <code>2.7.18</code> and the <code>python3</code> version to <code>3.10.1</code>. The global version is useful for user-wide modules and tools you might want to install with <code>pip</code>. You can get a list of possible versions with <code>pyenv install -l</code>.</p> <h2 id="managing-virtual-environments"><a href="#managing-virtual-environments"></a>Managing virtual environments</h2> <p>For specific projects, you probably only want to install the packages necessary for that project. A virtualenv is essentially an isolated installation of python (and packages) which are specific to the current environment. This lets you organize what modules you have installed, as well as the python version.</p> <p>As an example, let’s create a virtualenv named <code>my-venv</code>. First, create it with</p> <pre><code><kbd>pyenv virtualenv 3.10.1 my-venv</kbd></code></pre> <p>Let’s say we are in a project directory where I want to use the <code>my-venv</code> virtual environment. Simply create a file named <code>.python-version</code>, which is populated with the name of the desired virtual environment:</p> <pre><code><kbd>echo &quot;my-venv&quot; &gt; .python-version</kbd></code></pre> <p>Now, whenever you enter this directory, <code>pyenv</code> will automatically activate the environment, and whenever you leave, the environment will deactivate.</p> <p>You can also manually activate the virtual environment with</p> <pre><code><kbd>pyenv activate my-venv</kbd></code></pre> <p>and deactivate with</p> <pre><code><kbd>pyenv deactivate</kbd></code></pre> <p>To get a list of all the virtual environments currently installed, run</p> <pre><code><kbd>pyenv virtualenvs</kbd></code></pre> <p>You can uninstall virtual environments with</p> <pre><code><kbd>pyenv uninstall my-venv</kbd></code></pre> Previewing a Development Branch on Cloudflare Pages 2022-01-19T00:00:00+00:00 2022-01-19T00:00:00+00:00 https://rutar.org/writing/previewing-a-development-branch-on-cloudflare-pages/ <p>In this article, I assume that your repository is hosted on <a rel="noopener" target="_blank" href="https://github.com">GitHub</a>, and the site is hosted on <a rel="noopener" target="_blank" href="https://pages.cloudflare.com/">Cloudflare pages</a>. I’m sure other providers will work, with similar instructions.</p> <h2 id="creating-the-development-branch"><a href="#creating-the-development-branch"></a>Creating the development branch</h2> <p>First, create a new branch <strong>development</strong> (or any name you would like):</p> <pre><code><kbd>git pull</kbd> <kbd>git branch development</kbd> </code></pre> <p>Now to add draft changes, first</p> <pre><code><kbd>git switch development</kbd></code></pre> <p>then perform the changes you want and commit them. To include those changes in your main branch,</p> <pre><code><kbd>git switch main</kbd> <kbd>git merge development</kbd> <kbd>git push</kbd> </code></pre> <p>Now, we just need to push the <strong>development</strong> branch to our GitHub repository.</p> <pre><code><kbd>git push origin development</kbd></code></pre> <p>Cloudflare will automatically build changes to your development branch, which you can preview at the URL <code>development.&lt;project-name&gt;.pages.dev</code>.</p> <h2 id="customizing-the-build-command"><a href="#customizing-the-build-command"></a>Customizing the build command</h2> <p>It is often the case that you want the build command for your development branch to be different than the build command for your main branch. For example, if you use <a rel="noopener" target="_blank" href="https://getzola.org">Zola</a> for templating, you might build the main branch with</p> <pre><code><kbd>zola build</kbd></code></pre> <p>whereas you might build the development branch with</p> <pre><code><kbd>zola build --drafts</kbd></code></pre> <p>Moreover, when using Zola, the site URL is automatically set based on the key <code>base_url</code> in your <code>config.toml</code>. This works for the main branch, but our development branch has a different URL, which will cause non-relative internal links to be broken.</p> <p>Conveniently, Cloudflare Pages defines an environment variable <code>$CF_PAGES_BRANCH</code>, which is the name of the branch currently being built. We can use this variable to write a new build command with better behaviour:</p> <pre data-lang="sh" class="language-sh z-code"><code class="language-sh" data-lang="sh"><span class="z-source z-shell z-bash"><span class="z-keyword z-control z-conditional z-if z-shell">if</span> <span class="z-support z-function z-test z-begin z-shell">[</span><span class="z-meta z-function-call z-arguments z-shell"> <span class="z-string z-quoted z-double z-shell"><span class="z-punctuation z-definition z-string z-begin z-shell">&quot;</span><span class="z-meta z-group z-expansion z-parameter z-shell"><span class="z-punctuation z-definition z-variable z-shell">$</span><span class="z-variable z-other z-readwrite z-shell">CF_PAGES_BRANCH</span></span><span class="z-punctuation z-definition z-string z-end z-shell">&quot;</span></span> <span class="z-keyword z-operator z-logical z-shell">=</span> <span class="z-string z-quoted z-double z-shell"><span class="z-punctuation z-definition z-string z-begin z-shell">&quot;</span>main<span class="z-punctuation z-definition z-string z-end z-shell">&quot;</span></span> <span class="z-support z-function z-test z-end z-shell">]</span></span><span class="z-keyword z-operator z-logical z-continue z-shell">;</span> <span class="z-keyword z-control z-conditional z-then z-shell">then</span> <span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">zola</span></span><span class="z-meta z-function-call z-arguments z-shell"> build</span><span class="z-keyword z-operator z-logical z-continue z-shell">;</span> <span class="z-keyword z-control z-conditional z-else z-shell">else</span> <span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">zola</span></span><span class="z-meta z-function-call z-arguments z-shell"> build<span class="z-variable z-parameter z-option z-shell"><span class="z-punctuation z-definition z-parameter z-shell"> -</span>u</span> <span class="z-string z-quoted z-double z-shell"><span class="z-punctuation z-definition z-string z-begin z-shell">&quot;</span>https://<span class="z-meta z-group z-expansion z-parameter z-shell"><span class="z-punctuation z-definition z-variable z-shell">$</span><span class="z-variable z-other z-readwrite z-shell">CF_PAGES_BRANCH</span></span>.&lt;project-name&gt;.pages.dev<span class="z-punctuation z-definition z-string z-end z-shell">&quot;</span></span><span class="z-variable z-parameter z-option z-shell"><span class="z-punctuation z-definition z-parameter z-shell"> --</span>drafts</span></span><span class="z-keyword z-operator z-logical z-continue z-shell">;</span> <span class="z-keyword z-control z-conditional z-end z-shell">fi</span> </span></code></pre> <p>If the branch is your main branch, then just run <code>zola build</code>. Otherwise, the preview URL will be <code>https://development.&lt;project-name&gt;.pages.dev</code>, which we manually specify with the <code>-u</code> option. And we also include draft articles in the preview as well, with the <code>--drafts</code> option. If we decide to add more branches, the same non-main build command will work for any branch we add.</p> <p>Now, whenever you push commits on your development branch to GitHub, you can visit a preview of the build at <code>https://development.&lt;project-name&gt;.pages.dev</code>.</p>